Merge pull request #27800 from noahjacob/buy_on
refactor: updated buying onboarding tours.
diff --git a/.eslintrc b/.eslintrc
index cb45ce5..46fb354 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -154,7 +154,8 @@
"before": true,
"beforeEach": true,
"onScan": true,
+ "html2canvas": true,
"extend_cscript": true,
- "localforage": true,
+ "localforage": true
}
}
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index e7fa354..6eaf9cc 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -13,3 +13,10 @@
# This commit just changes spaces to tabs for indentation in some files
5f473611bd6ed57703716244a054d3fb5ba9cd23
+
+# Whitespace fix throughout codebase
+4551d7d6029b6f587f6c99d4f8df5519241c6a86
+b147b85e6ac19a9220cd1e2958a6ebd99373283a
+
+# sort and cleanup imports
+915b34391c2066dfc83e60a5813c5a877cebe7ac
diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict
new file mode 100644
index 0000000..a79137d
--- /dev/null
+++ b/.github/helper/.flake8_strict
@@ -0,0 +1,72 @@
+[flake8]
+ignore =
+ B007,
+ B009,
+ B010,
+ B950,
+ E101,
+ E111,
+ E114,
+ E116,
+ E117,
+ E121,
+ E122,
+ E123,
+ E124,
+ E125,
+ E126,
+ E127,
+ E128,
+ E131,
+ E201,
+ E202,
+ E203,
+ E211,
+ E221,
+ E222,
+ E223,
+ E224,
+ E225,
+ E226,
+ E228,
+ E231,
+ E241,
+ E242,
+ E251,
+ E261,
+ E262,
+ E265,
+ E266,
+ E271,
+ E272,
+ E273,
+ E274,
+ E301,
+ E302,
+ E303,
+ E305,
+ E306,
+ E402,
+ E501,
+ E502,
+ E701,
+ E702,
+ E703,
+ E741,
+ F403,
+ W191,
+ W291,
+ W292,
+ W293,
+ W391,
+ W503,
+ W504,
+ E711,
+ E129,
+ F841,
+ E713,
+ E712,
+
+
+max-line-length = 200
+exclude=.github/helper/semgrep_rules,test_*.py
diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py
index 9cc4663..378983e 100644
--- a/.github/helper/documentation.py
+++ b/.github/helper/documentation.py
@@ -24,6 +24,8 @@
parts = parsed_url.path.split('/')
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
return True
+ elif parsed_url.netloc == "docs.erpnext.com":
+ return True
if __name__ == "__main__":
@@ -32,11 +34,15 @@
if response.ok:
payload = response.json()
- title = payload.get("title", "").lower()
- head_sha = payload.get("head", {}).get("sha")
- body = payload.get("body", "").lower()
+ title = (payload.get("title") or "").lower().strip()
+ head_sha = (payload.get("head") or {}).get("sha")
+ body = (payload.get("body") or "").lower()
- if title.startswith("feat") and head_sha and "no-docs" not in body:
+ if (title.startswith("feat")
+ and head_sha
+ and "no-docs" not in body
+ and "backport" not in body
+ ):
if docs_link_exists(body):
print("Documentation Link Found. You're Awesome! 🎉")
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index 455ab86..ac623e9 100644
--- a/.github/helper/install.sh
+++ b/.github/helper/install.sh
@@ -4,11 +4,7 @@
cd ~ || exit
-sudo apt-get install redis-server
-
-sudo apt install nodejs
-
-sudo apt install npm
+sudo apt-get install redis-server libcups2-dev
pip install frappe-bench
@@ -32,7 +28,6 @@
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
sudo chmod o+x /usr/local/bin/wkhtmltopdf
-sudo apt-get install libcups2-dev
cd ~/frappe-bench || exit
diff --git a/.github/helper/semgrep_rules/frappe_correctness.py b/.github/helper/semgrep_rules/frappe_correctness.py
index 745e646..83d4acf 100644
--- a/.github/helper/semgrep_rules/frappe_correctness.py
+++ b/.github/helper/semgrep_rules/frappe_correctness.py
@@ -1,5 +1,5 @@
import frappe
-from frappe import _, flt
+from frappe import _
from frappe.model.document import Document
diff --git a/.github/helper/semgrep_rules/report.py b/.github/helper/semgrep_rules/report.py
new file mode 100644
index 0000000..ff27840
--- /dev/null
+++ b/.github/helper/semgrep_rules/report.py
@@ -0,0 +1,15 @@
+from frappe import _
+
+
+# ruleid: frappe-missing-translate-function-in-report-python
+{"label": "Field Label"}
+
+# ruleid: frappe-missing-translate-function-in-report-python
+dict(label="Field Label")
+
+
+# ok: frappe-missing-translate-function-in-report-python
+{"label": _("Field Label")}
+
+# ok: frappe-missing-translate-function-in-report-python
+dict(label=_("Field Label"))
diff --git a/.github/helper/semgrep_rules/report.yml b/.github/helper/semgrep_rules/report.yml
new file mode 100644
index 0000000..f2a9b16
--- /dev/null
+++ b/.github/helper/semgrep_rules/report.yml
@@ -0,0 +1,34 @@
+rules:
+- id: frappe-missing-translate-function-in-report-python
+ paths:
+ include:
+ - "**/report"
+ exclude:
+ - "**/regional"
+ pattern-either:
+ - patterns:
+ - pattern: |
+ {..., "label": "...", ...}
+ - pattern-not: |
+ {..., "label": _("..."), ...}
+ - patterns:
+ - pattern: dict(..., label="...", ...)
+ - pattern-not: dict(..., label=_("..."), ...)
+ message: |
+ All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
+ languages: [python]
+ severity: ERROR
+
+- id: frappe-translated-values-in-business-logic
+ paths:
+ include:
+ - "**/report"
+ patterns:
+ - pattern-inside: |
+ {..., filters: [...], ...}
+ - pattern: |
+ {..., options: [..., __("..."), ...], ...}
+ message: |
+ Using translated values in options field will require you to translate the values while comparing in business logic. Instead of passing translated labels provide objects that contain both label and value. e.g. { label: __("Option value"), value: "Option value"}
+ languages: [javascript]
+ severity: ERROR
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
new file mode 100644
index 0000000..16e490a
--- /dev/null
+++ b/.github/workflows/linters.yml
@@ -0,0 +1,27 @@
+name: Linters
+
+on:
+ pull_request: { }
+
+jobs:
+
+ linters:
+ name: linters
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: returntocorp/semgrep-action@v1
+ env:
+ SEMGREP_TIMEOUT: 120
+ with:
+ config: >-
+ r/python.lang.correctness
+ .github/helper/semgrep_rules
+
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+
+ - name: Install and Run Pre-commit
+ uses: pre-commit/action@v2.0.3
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index 72d4028..92a1962 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -7,10 +7,13 @@
- '**.md'
workflow_dispatch:
+concurrency:
+ group: patch-develop-${{ github.event.number }}
+ cancel-in-progress: true
jobs:
test:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-latest
timeout-minutes: 60
name: Patch Test
@@ -31,7 +34,13 @@
- name: Setup Python
uses: actions/setup-python@v2
with:
- python-version: 3.6
+ python-version: 3.7
+
+ - name: Setup Node
+ 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
diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml
deleted file mode 100644
index e27b406..0000000
--- a/.github/workflows/semgrep.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: Semgrep
-
-on:
- pull_request: { }
-
-jobs:
- semgrep:
- name: Frappe Linter
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: returntocorp/semgrep-action@v1
- env:
- SEMGREP_TIMEOUT: 120
- with:
- config: >-
- r/python.lang.correctness
- .github/helper/semgrep_rules
diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml
index 3a1ecd3..4f84b86 100644
--- a/.github/workflows/server-tests.yml
+++ b/.github/workflows/server-tests.yml
@@ -12,9 +12,13 @@
- '**.js'
- '**.md'
+concurrency:
+ group: server-develop-${{ github.event.number }}
+ cancel-in-progress: true
+
jobs:
test:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
@@ -43,6 +47,12 @@
with:
python-version: 3.7
+ - name: Setup Node
+ 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
@@ -89,34 +99,10 @@
CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
- - name: Upload Coverage Data
- run: |
- cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
- cd ${GITHUB_WORKSPACE}
- pip3 install coverage==5.5
- pip3 install coveralls==3.0.1
- coveralls
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
- COVERALLS_FLAG_NAME: run-${{ matrix.container }}
- COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }}
- COVERALLS_PARALLEL: true
-
- coveralls:
- name: Coverage Wrap Up
- needs: test
- container: python:3-slim
- runs-on: ubuntu-18.04
- steps:
- - name: Clone
- uses: actions/checkout@v2
-
- - name: Coveralls Finished
- run: |
- cd ${GITHUB_WORKSPACE}
- pip3 install coverage==5.5
- pip3 install coveralls==3.0.1
- coveralls --finish
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - 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/translation_linter.yml b/.github/workflows/translation_linter.yml
deleted file mode 100644
index 4becaeb..0000000
--- a/.github/workflows/translation_linter.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Frappe Linter
-on:
- pull_request:
- branches:
- - develop
- - version-12-hotfix
- - version-11-hotfix
-jobs:
- check_translation:
- name: Translation Syntax Check
- runs-on: ubuntu-18.04
- steps:
- - uses: actions/checkout@v2
- - name: Setup python3
- uses: actions/setup-python@v1
- with:
- python-version: 3.6
- - name: Validating Translation Syntax
- run: |
- git fetch origin $GITHUB_BASE_REF:$GITHUB_BASE_REF -q
- files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF)
- python $GITHUB_WORKSPACE/.github/helper/translation.py $files
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
index 3959268..d765f04 100644
--- a/.github/workflows/ui-tests.yml
+++ b/.github/workflows/ui-tests.yml
@@ -6,9 +6,13 @@
- '**.md'
workflow_dispatch:
+concurrency:
+ group: ui-develop-${{ github.event.number }}
+ cancel-in-progress: true
+
jobs:
test:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
@@ -95,16 +99,18 @@
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 --no-lockfile
+ 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: ${{ secrets.CYPRESS_RECORD_KEY }}
+ CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd
- name: Show bench console if tests failed
if: ${{ failure() }}
diff --git a/.mergify.yml b/.mergify.yml
new file mode 100644
index 0000000..f3d0409
--- /dev/null
+++ b/.mergify.yml
@@ -0,0 +1,58 @@
+pull_request_rules:
+ - name: Auto-close PRs on stable branch
+ conditions:
+ - and:
+ - and:
+ - author!=surajshetty3416
+ - author!=gavindsouza
+ - author!=rohitwaghchaure
+ - author!=nabinhait
+ - or:
+ - base=version-13
+ - base=version-12
+ actions:
+ close:
+ comment:
+ message: |
+ @{{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: backport to version-13-hotfix
+ conditions:
+ - label="backport version-13-hotfix"
+ actions:
+ backport:
+ branches:
+ - version-13-hotfix
+ assignees:
+ - "{{ author }}"
+
+ - name: backport to version-13-pre-release
+ conditions:
+ - label="backport version-13-pre-release"
+ actions:
+ backport:
+ branches:
+ - version-13-pre-release
+ assignees:
+ - "{{ author }}"
+
+ - name: backport to version-12-hotfix
+ conditions:
+ - label="backport version-12-hotfix"
+ actions:
+ backport:
+ branches:
+ - version-12-hotfix
+ assignees:
+ - "{{ author }}"
+
+ - name: backport to version-12-pre-release
+ conditions:
+ - label="backport version-12-pre-release"
+ actions:
+ backport:
+ branches:
+ - version-12-pre-release
+ assignees:
+ - "{{ author }}"
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..b74d9a6
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,38 @@
+exclude: 'node_modules|.git'
+default_stages: [commit]
+fail_fast: false
+
+
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.0.1
+ hooks:
+ - id: trailing-whitespace
+ files: "erpnext.*"
+ exclude: ".*json$|.*txt$|.*csv|.*md"
+ - id: check-yaml
+ - id: no-commit-to-branch
+ args: ['--branch', 'develop']
+ - id: check-merge-conflict
+ - id: check-ast
+
+ - repo: https://gitlab.com/pycqa/flake8
+ rev: 3.9.2
+ hooks:
+ - id: flake8
+ additional_dependencies: [
+ 'flake8-bugbear',
+ ]
+ args: ['--config', '.github/helper/.flake8_strict']
+ exclude: ".*setup.py$"
+
+ - repo: https://github.com/timothycrosley/isort
+ rev: 5.9.1
+ hooks:
+ - id: isort
+ exclude: ".*setup.py$"
+
+ci:
+ autoupdate_schedule: weekly
+ skip: []
+ submodules: false
diff --git a/.snyk b/.snyk
deleted file mode 100644
index 140f3ed..0000000
--- a/.snyk
+++ /dev/null
@@ -1,8 +0,0 @@
-# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
-version: v1.14.0
-ignore: {}
-# patches apply the minimum changes required to fix a vulnerability
-patch:
- SNYK-JS-LODASH-450202:
- - cypress > getos > async > lodash:
- patched: '2020-01-31T01:35:12.802Z'
diff --git a/README.md b/README.md
index c6fc251..87d7d73 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
[![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)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
-[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)
+[![codecov](https://codecov.io/gh/frappe/erpnext/branch/develop/graph/badge.svg?token=0TwvyUg3I5)](https://codecov.io/gh/frappe/erpnext)
[https://erpnext.com](https://erpnext.com)
@@ -77,6 +77,12 @@
---
+## Learning
+
+1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
+
+---
+
## Logo and Trademark
The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..67bd445
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,17 @@
+codecov:
+ require_ci_to_pass: yes
+
+coverage:
+ status:
+ project:
+ default:
+ target: auto
+ threshold: 0.5%
+
+comment:
+ layout: "diff, files"
+ require_changes: true
+ after_n_builds: 3
+
+ignore:
+ - "erpnext/demo"
diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js
new file mode 100644
index 0000000..79e08b3
--- /dev/null
+++ b/cypress/integration/test_organizational_chart_desktop.js
@@ -0,0 +1,116 @@
+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{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
new file mode 100644
index 0000000..161fae0
--- /dev/null
+++ b/cypress/integration/test_organizational_chart_mobile.js
@@ -0,0 +1,195 @@
+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{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/erpnext/.stylelintrc b/erpnext/.stylelintrc
index 1e05d1f..30075f1 100644
--- a/erpnext/.stylelintrc
+++ b/erpnext/.stylelintrc
@@ -6,4 +6,4 @@
"scss/at-rule-no-unknown": true,
"no-descending-specificity": null
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index c90e01c..4521827 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-import inspect
-import frappe
-from erpnext.hooks import regional_overrides
-from frappe.utils import getdate
-__version__ = '13.8.0'
+import inspect
+
+import frappe
+
+from erpnext.hooks import regional_overrides
+
+__version__ = '13.9.0'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/custom/address.py b/erpnext/accounts/custom/address.py
index c417a49..a6d08d8 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -1,7 +1,11 @@
import frappe
from frappe import _
-from frappe.contacts.doctype.address.address import Address
-from frappe.contacts.doctype.address.address import get_address_templates
+from frappe.contacts.doctype.address.address import (
+ Address,
+ get_address_display,
+ get_address_templates,
+)
+
class ERPNextAddress(Address):
def validate(self):
@@ -22,6 +26,16 @@
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 }
+ 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):
filters = [
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js
index e12eae9..d8a83e5 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js
@@ -19,4 +19,4 @@
reqd: 1
},
]
-};
\ No newline at end of file
+};
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 85f54f9..5eb8577 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
@@ -2,14 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, json
+
+import frappe
from frappe import _
-from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form
-from erpnext.accounts.report.general_ledger.general_ledger import execute
+from frappe.utils import add_to_date, formatdate, get_link_to_form, getdate, nowdate
from frappe.utils.dashboard import cache_source
from frappe.utils.dateutils import get_from_date_from_timespan, get_period_ending
from frappe.utils.nestedset import get_descendants_of
+
@frappe.whitelist()
@cache_source
def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 335e8a1..71957e6 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -2,11 +2,26 @@
import frappe
from frappe import _
-from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded
-from erpnext.accounts.utils import get_account_currency
from frappe.email import sendmail_to_system_managers
-from frappe.utils.background_jobs import enqueue
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ date_diff,
+ flt,
+ get_first_day,
+ get_last_day,
+ get_link_to_form,
+ getdate,
+ rounded,
+ today,
+)
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
+from erpnext.accounts.utils import get_account_currency
+
def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
@@ -359,12 +374,15 @@
try:
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
frappe.db.commit()
- except:
- frappe.db.rollback()
- traceback = frappe.get_traceback()
- frappe.log_error(message=traceback)
+ except Exception as e:
+ if frappe.flags.in_test:
+ raise e
+ else:
+ frappe.db.rollback()
+ traceback = frappe.get_traceback()
+ frappe.log_error(message=traceback)
- frappe.flags.deferred_accounting_error = True
+ frappe.flags.deferred_accounting_error = True
def send_mail(deferred_process):
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
@@ -430,7 +448,7 @@
if submit:
journal_entry.submit()
- except:
+ except Exception:
frappe.db.rollback()
traceback = frappe.get_traceback()
frappe.log_error(message=traceback)
@@ -450,5 +468,3 @@
return debit_account
else:
return credit_account
-
-
diff --git a/erpnext/accounts/doctype/account/account.js b/erpnext/accounts/doctype/account/account.js
index f7f1a5f..7a1d735 100644
--- a/erpnext/accounts/doctype/account/account.js
+++ b/erpnext/accounts/doctype/account/account.js
@@ -74,7 +74,7 @@
});
} else if (cint(frm.doc.is_group) == 0
&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
- cur_frm.add_custom_button(__('Ledger'), function () {
+ frm.add_custom_button(__('Ledger'), function () {
frappe.route_options = {
"account": frm.doc.name,
"from_date": frappe.sys_defaults.year_start_date,
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index f763df0..605262f 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -2,11 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _, throw
from frappe.utils import cint, cstr
-from frappe import throw, _
from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
+import erpnext
+
+
class RootNotEditable(frappe.ValidationError): pass
class BalanceMismatchError(frappe.ValidationError): pass
@@ -194,7 +198,7 @@
"company": company,
# parent account's currency should be passed down to child account's curreny
# if it is None, it picks it up from default company currency, which might be unintended
- "account_currency": self.account_currency,
+ "account_currency": erpnext.get_company_currency(company),
"parent_account": parent_acc_name_map[company]
})
@@ -205,8 +209,7 @@
# update the parent company's value in child companies
doc = frappe.get_doc("Account", child_account)
parent_value_changed = False
- for field in ['account_type', 'account_currency',
- 'freeze_account', 'balance_must_be']:
+ for field in ['account_type', 'freeze_account', 'balance_must_be']:
if doc.get(field) != self.get(field):
parent_value_changed = True
doc.set(field, self.get(field))
diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index 7516134..a4b6e0b 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -45,6 +45,49 @@
],
root_label: "Accounts",
get_tree_nodes: 'erpnext.accounts.utils.get_children',
+ on_get_node: function(nodes, deep=false) {
+ if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
+
+ let accounts = [];
+ if (deep) {
+ // in case of `get_all_nodes`
+ accounts = nodes.reduce((acc, node) => [...acc, ...node.data], []);
+ } else {
+ accounts = nodes;
+ }
+
+ const get_balances = frappe.call({
+ method: 'erpnext.accounts.utils.get_account_balances',
+ args: {
+ accounts: accounts,
+ company: cur_tree.args.company
+ },
+ });
+
+ get_balances.then(r => {
+ if (!r.message || r.message.length == 0) return;
+
+ for (let account of r.message) {
+
+ const node = cur_tree.nodes && cur_tree.nodes[account.value];
+ if (!node || node.is_root) continue;
+
+ // show Dr if positive since balance is calculated as debit - credit else show Cr
+ const balance = account.balance_in_account_currency || account.balance;
+ const dr_or_cr = balance > 0 ? "Dr": "Cr";
+ const format = (value, currency) => format_currency(Math.abs(value), currency);
+
+ if (account.balance!==undefined) {
+ $('<span class="balance-area pull-right">'
+ + (account.balance_in_account_currency ?
+ (format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
+ + format(account.balance, account.company_currency)
+ + " " + dr_or_cr
+ + '</span>').insertBefore(node.$ul);
+ }
+ }
+ });
+ },
add_tree_node: 'erpnext.accounts.utils.add_ac',
menu_items:[
{
@@ -122,24 +165,6 @@
}
}, "add");
},
- onrender: function(node) {
- if (frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
-
- // show Dr if positive since balance is calculated as debit - credit else show Cr
- let balance = node.data.balance_in_account_currency || node.data.balance;
- let dr_or_cr = balance > 0 ? "Dr": "Cr";
-
- if (node.data && node.data.balance!==undefined) {
- $('<span class="balance-area pull-right">'
- + (node.data.balance_in_account_currency ?
- (format_currency(Math.abs(node.data.balance_in_account_currency),
- node.data.account_currency) + " / ") : "")
- + format_currency(Math.abs(node.data.balance), node.data.company_currency)
- + " " + dr_or_cr
- + '</span>').insertBefore(node.$ul);
- }
- }
- },
toolbar: [
{
label:__("Add Child"),
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 927adc7..d6ccd16 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
@@ -2,12 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+import json
+import os
-import frappe, os, json
+import frappe
from frappe.utils import cstr
-from unidecode import unidecode
-from six import iteritems
from frappe.utils.nestedset import rebuild_tree
+from six import iteritems
+from unidecode import unidecode
+
def create_charts(company, chart_template=None, existing_company=None, custom_chart=None):
chart = custom_chart or get_chart(chart_template, existing_company)
@@ -91,11 +94,14 @@
return get_account_tree_from_existing_company(existing_company)
elif chart_template == "Standard":
- from erpnext.accounts.doctype.account.chart_of_accounts.verified import standard_chart_of_accounts
+ 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
+ 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",)
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 eb3e7ff..7222357 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
@@ -6,12 +6,13 @@
"""
from __future__ import print_function, unicode_literals
-import os, json
import ast
+import json
+import os
from xml.etree import ElementTree as ET
-from frappe.utils.csvutils import read_csv_content
-import frappe
+import frappe
+from frappe.utils.csvutils import read_csv_content
from six import iteritems
path = "/Users/nabinhait/projects/odoo/addons"
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 161e52a..f058afb 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get():
return {
_("Application of Funds (Assets)"): {
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 acb11e5..9f33952 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get():
return {
_("Application of Funds (Assets)"): {
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 533eda3..0c3b33e 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -2,10 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
-from erpnext.accounts.doctype.account.account import update_account_number, merge_account
+
+from erpnext.accounts.doctype.account.account import merge_account, update_account_number
+from erpnext.stock import get_company_default_inventory_account, get_warehouse_account
+
class TestAccount(unittest.TestCase):
def test_rename_account(self):
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
index 65c5ff1..2fa1d53 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -60,4 +60,4 @@
let row = locals[cdt][cdn];
row.reference_document = frm.doc.document_type;
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index fac28c9..af8255f 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -3,15 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
+
import json
-from frappe.model.document import Document
+
+import frappe
+from frappe import _, scrub
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-from frappe import scrub
-from frappe.utils import cstr
-from frappe.utils.background_jobs import enqueue
from frappe.model import core_doctypes_list
+from frappe.model.document import Document
+from frappe.utils import cstr
+
class AccountingDimension(Document):
def before_insert(self):
@@ -47,9 +48,9 @@
def on_trash(self):
if frappe.flags.in_test:
- delete_accounting_dimension(doc=self, queue='long')
+ delete_accounting_dimension(doc=self)
else:
- frappe.enqueue(delete_accounting_dimension, doc=self)
+ frappe.enqueue(delete_accounting_dimension, doc=self, queue='long')
def set_fieldname_and_label(self):
if not self.label:
diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
index e657a9a..3769fc1 100644
--- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -3,10 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
+import frappe
+
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']
@@ -113,5 +115,3 @@
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
dimension2.disabled = 1
dimension2.save()
-
-
diff --git a/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.py b/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.py
index 17cf549..c116f16 100644
--- a/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.py
+++ b/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AccountingDimensionDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
index 74b7b51..9dd882a 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
@@ -79,4 +79,4 @@
row.accounting_dimension = frm.doc.accounting_dimension;
frm.refresh_field("dimensions");
}
-});
\ No newline at end of file
+});
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 6aef9ca..cb8c7d1 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, scrub
from frappe.model.document import Document
+
class AccountingDimensionFilter(Document):
def validate(self):
self.validate_applicable_accounts()
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 7f6254f..9968f68 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
@@ -3,10 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
+from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
+ create_dimension,
+ disable_dimension,
+)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
test_dependencies = ['Location', 'Cost Center', 'Department']
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index 63b5dbb..67d1a8a 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
class OverlapError(frappe.ValidationError): pass
@@ -56,4 +58,4 @@
self.append('closed_documents', {
"document_type": doctype_for_closing.document_type,
"closed": doctype_for_closing.closed
- })
\ No newline at end of file
+ })
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.js b/erpnext/accounts/doctype/accounting_period/test_accounting_period.js
deleted file mode 100644
index 71ce5b8..0000000
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Accounting Period", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Accounting Period
- () => frappe.tests.make('Accounting Period', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index dc472c7..5885b58 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -3,12 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, add_months
-from erpnext.accounts.general_ledger import ClosedAccountingPeriod
+
+import frappe
+from frappe.utils import add_months, nowdate
+
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.general_ledger import ClosedAccountingPeriod
test_dependencies = ['Item']
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
index 541901c..0627675 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js
@@ -6,46 +6,3 @@
}
});
-
-frappe.tour['Accounts Settings'] = [
- {
- fieldname: "acc_frozen_upto",
- title: "Accounts Frozen Upto",
- description: __("Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role."),
- },
- {
- fieldname: "frozen_accounts_modifier",
- title: "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
- description: __("Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.")
- },
- {
- fieldname: "determine_address_tax_category_from",
- title: "Determine Address Tax Category From",
- description: __("Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.")
- },
- {
- fieldname: "over_billing_allowance",
- title: "Over Billing Allowance Percentage",
- description: __("The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.")
- },
- {
- fieldname: "credit_controller",
- title: "Credit Controller",
- description: __("Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.")
- },
- {
- fieldname: "make_payment_via_journal_entry",
- title: "Make Payment via Journal Entry",
- description: __("When checked, if user proceeds to make payment from an invoice, the system will open a Journal Entry instead of a Payment Entry.")
- },
- {
- fieldname: "unlink_payment_on_cancellation_of_invoice",
- title: "Unlink Payment on Cancellation of Invoice",
- description: __("If checked, system will unlink the payment against the respective invoice.")
- },
- {
- fieldname: "unlink_advance_payment_on_cancelation_of_order",
- title: "Unlink Advance Payment on Cancellation of Order",
- description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.")
- }
-];
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 9adce3c..7d0ecfb 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -19,6 +19,7 @@
"delete_linked_ledger_entries",
"book_asset_depreciation_entry_automatically",
"unlink_advance_payment_on_cancelation_of_order",
+ "enable_common_party_accounting",
"post_change_gl_entries",
"enable_discount_accounting",
"tax_settings_section",
@@ -268,6 +269,12 @@
"fieldname": "enable_discount_accounting",
"fieldtype": "Check",
"label": "Enable Discount Accounting"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_common_party_accounting",
+ "fieldtype": "Check",
+ "label": "Enable Common Party Accounting"
}
],
"icon": "icon-cog",
@@ -275,7 +282,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-08-09 13:08:01.335416",
+ "modified": "2021-08-19 11:17:38.788054",
"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 5544913..f544733 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -4,11 +4,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cint
-from frappe.model.document import Document
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+from frappe.model.document import Document
+from frappe.utils import cint
class AccountsSettings(Document):
@@ -37,7 +38,7 @@
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:
@@ -52,4 +53,4 @@
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)
\ No newline at end of file
+ make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
diff --git a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js
index d47d6e5..3e38386 100644
--- a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js
+++ b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js
@@ -5,4 +5,4 @@
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
frm.set_df_property("credit_controller", "label", "Credit Manager");
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
index 014cf45..c1c156f 100644
--- a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import unittest
import frappe
diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py
index 597d2cc..0c98f24 100644
--- a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py
+++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AdvanceTaxesandCharges(Document):
pass
diff --git a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py
index c2afc1a..b5e7ad3 100644
--- a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py
+++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AllowedDimension(Document):
pass
diff --git a/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.py b/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.py
index a3e4bbc..3e84c30 100644
--- a/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.py
+++ b/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class AllowedToTransactWith(Document):
pass
diff --git a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py
index 0fccaf3..91331fa 100644
--- a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py
+++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ApplicableOnAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js
index 19041a3..059e1d3 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -120,4 +120,4 @@
plaid_success(token, response) {
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/bank/bank.py b/erpnext/accounts/doctype/bank/bank.py
index 41aae14..e1eb984 100644
--- a/erpnext/accounts/doctype/bank/bank.py
+++ b/erpnext/accounts/doctype/bank/bank.py
@@ -3,9 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
+from frappe.contacts.address_and_contact import (
+ delete_contact_and_address,
+ load_address_and_contact,
+)
from frappe.model.document import Document
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+
class Bank(Document):
def onload(self):
@@ -13,4 +17,4 @@
load_address_and_contact(self)
def on_trash(self):
- delete_contact_and_address('Bank', self.name)
\ No newline at end of file
+ delete_contact_and_address('Bank', self.name)
diff --git a/erpnext/accounts/doctype/bank/test_bank.js b/erpnext/accounts/doctype/bank/test_bank.js
deleted file mode 100644
index 9ec2644..0000000
--- a/erpnext/accounts/doctype/bank/test_bank.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank
- () => frappe.tests.make('Bank', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank/test_bank.py b/erpnext/accounts/doctype/bank/test_bank.py
index d8741f2..62d14d6 100644
--- a/erpnext/accounts/doctype/bank/test_bank.py
+++ b/erpnext/accounts/doctype/bank/test_bank.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestBank(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index 3e08c28..703f55d 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -3,10 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+from frappe.contacts.address_and_contact import (
+ delete_contact_and_address,
+ load_address_and_contact,
+)
from frappe.model.document import Document
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+
class BankAccount(Document):
def onload(self):
diff --git a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
index a959cea..c7ea152 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py
@@ -26,4 +26,4 @@
'items': ['Journal Entry']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.js b/erpnext/accounts/doctype/bank_account/test_bank_account.js
deleted file mode 100644
index c20a799..0000000
--- a/erpnext/accounts/doctype/bank_account/test_bank_account.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Account", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Account
- () => frappe.tests.make('Bank Account', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py
index ed34d17..dc970f3 100644
--- a/erpnext/accounts/doctype/bank_account/test_bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py
@@ -3,11 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe import ValidationError
import unittest
+import frappe
+from frappe import ValidationError
+
# test_records = frappe.get_test_records('Bank Account')
class TestBankAccount(unittest.TestCase):
diff --git a/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.py b/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.py
index ab52c4a..84fa0c9 100644
--- a/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.py
+++ b/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class BankAccountSubtype(Document):
pass
diff --git a/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.js b/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.js
deleted file mode 100644
index f599998..0000000
--- a/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Account Subtype", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Account Subtype
- () => frappe.tests.make('Bank Account Subtype', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.py b/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.py
index ca3addc..d4eb88b 100644
--- a/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.py
+++ b/erpnext/accounts/doctype/bank_account_subtype/test_bank_account_subtype.py
@@ -5,5 +5,6 @@
import unittest
+
class TestBankAccountSubtype(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_account_type/bank_account_type.py b/erpnext/accounts/doctype/bank_account_type/bank_account_type.py
index b7dc0e0..bba43dc 100644
--- a/erpnext/accounts/doctype/bank_account_type/bank_account_type.py
+++ b/erpnext/accounts/doctype/bank_account_type/bank_account_type.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class BankAccountType(Document):
pass
diff --git a/erpnext/accounts/doctype/bank_account_type/test_bank_account_type.py b/erpnext/accounts/doctype/bank_account_type/test_bank_account_type.py
index f04725a..00fd433 100644
--- a/erpnext/accounts/doctype/bank_account_type/test_bank_account_type.py
+++ b/erpnext/accounts/doctype/bank_account_type/test_bank_account_type.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestBankAccountType(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
index ba3f2fa..63cc465 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
@@ -8,7 +8,7 @@
onload: function(frm) {
- let default_bank_account = frappe.defaults.get_user_default("Company")?
+ let default_bank_account = frappe.defaults.get_user_default("Company")?
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
frm.set_value("account", default_bank_account);
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 79f5596..340b448 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt, getdate, nowdate, fmt_money
-from frappe import msgprint, _
+from frappe import _, msgprint
from frappe.model.document import Document
+from frappe.utils import flt, fmt_money, getdate, nowdate
form_grid_templates = {
"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"
diff --git a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
index 833abde..bdf3c7f 100644
--- a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestBankClearance(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
index ecc5367..1b23400 100644
--- a/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
+++ b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class BankClearanceDetail(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
index 88e1055..8043c5f 100644
--- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py
@@ -3,10 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.model.document import Document
+
+import json
+
+import frappe
from frappe import _
from frappe.desk.search import sanitize_searchfield
+from frappe.model.document import Document
+
class BankGuarantee(Document):
def validate(self):
@@ -25,6 +29,6 @@
def get_vouchar_detials(column_list, doctype, docname):
column_list = json.loads(column_list)
for col in column_list:
- sanitize_searchfield(col)
+ sanitize_searchfield(col)
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
diff --git a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.js b/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.js
deleted file mode 100644
index 0c60920..0000000
--- a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Guarantee", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Guarantee
- () => frappe.tests.make('Bank Guarantee', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py
index 816743a..5cd455f 100644
--- a/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py
+++ b/erpnext/accounts/doctype/bank_guarantee/test_bank_guarantee.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestBankGuarantee(unittest.TestCase):
pass
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 8a17233..ce64ee4 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -3,17 +3,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import json
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
from frappe.utils import flt
from erpnext import get_company_currency
-from erpnext.accounts.utils import get_balance_on
-from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import get_entries, get_amounts_not_reflected_in_system
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
+from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
+ get_amounts_not_reflected_in_system,
+ get_entries,
+)
+from erpnext.accounts.utils import get_balance_on
class BankReconciliationTool(Document):
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
index d96950a..55b83b6 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestBankReconciliationTool(unittest.TestCase):
pass
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 ffc9d1c..25d1023 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -3,22 +3,22 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import csv
import json
import re
+import frappe
import openpyxl
+from frappe import _
+from frappe.core.doctype.data_import.data_import import DataImport
+from frappe.core.doctype.data_import.importer import Importer, ImportFile
+from frappe.utils.background_jobs import enqueue
+from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter
from six import string_types
-import frappe
-from frappe.core.doctype.data_import.importer import Importer, ImportFile
-from frappe.utils.background_jobs import enqueue
-from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE
-from frappe import _
-
-from frappe.core.doctype.data_import.data_import import DataImport
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
diff --git a/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
index cd58314..5b45fa2 100644
--- a/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestBankStatementImport(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index 5246baa..c4cf37e 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from erpnext.controllers.status_updater import StatusUpdater
from frappe.utils import flt
from six.moves import reduce
-from frappe import _
+
+from erpnext.controllers.status_updater import StatusUpdater
+
class BankTransaction(StatusUpdater):
def after_insert(self):
@@ -22,6 +24,10 @@
self.clear_linked_payment_entries()
self.set_status(update=True)
+ def on_cancel(self):
+ self.clear_linked_payment_entries(for_cancel=True)
+ self.set_status(update=True)
+
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])
@@ -42,20 +48,45 @@
self.reload()
- def clear_linked_payment_entries(self):
+ 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"]:
- self.clear_simple_entry(payment_entry)
+ self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
elif payment_entry.payment_document == "Sales Invoice":
- self.clear_sales_invoice(payment_entry)
+ self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
- def clear_simple_entry(self, payment_entry):
- frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
+ 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 len(get_reconciled_bank_transactions(payment_entry)) < 2:
+ return
- def clear_sales_invoice(self, payment_entry):
- frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.payment_document,
- parent=payment_entry.payment_entry), "clearance_date", self.date)
+ 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)
+
+ 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)
+
+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']
+ )
+
+ return reconciled_bank_transactions
def get_total_allocated_amount(payment_entry):
return frappe.db.sql("""
@@ -105,4 +136,3 @@
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
return doc.payment_entry
-
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js
index 2ecc2b0..2585ee9 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js
@@ -4,10 +4,12 @@
frappe.listview_settings['Bank Transaction'] = {
add_fields: ["unallocated_amount"],
get_indicator: function(doc) {
- if(flt(doc.unallocated_amount)>0) {
- return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
+ if(doc.docstatus == 2) {
+ return [__("Cancelled"), "red", "docstatus,=,2"];
} else if(flt(doc.unallocated_amount)<=0) {
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
+ } else if(flt(doc.unallocated_amount)>0) {
+ return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
index 33ae454..e8d032b 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
from frappe.utils import getdate
from frappe.utils.dateutils import parse_date
from six import iteritems
+
@frappe.whitelist()
def upload_bank_statement():
if getattr(frappe, "uploaded_file", None):
@@ -77,4 +80,4 @@
mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
- return mapping
\ No newline at end of file
+ return mapping
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js
deleted file mode 100644
index 305119e..0000000
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Transaction", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Bank Transaction
- () => frappe.tests.make('Bank Transaction', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index ce149f9..35d08bd 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -3,14 +3,19 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
import json
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+import unittest
+
+import frappe
+
+from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
+ get_linked_payments,
+ reconcile_vouchers,
+)
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
-from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import reconcile_vouchers, get_linked_payments
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
test_dependencies = ["Item", "Cost Center"]
@@ -25,7 +30,8 @@
def tearDownClass(cls):
for bt in frappe.get_all("Bank Transaction"):
doc = frappe.get_doc("Bank Transaction", bt.name)
- doc.cancel()
+ if doc.docstatus == 1:
+ doc.cancel()
doc.delete()
# Delete directly in DB to avoid validation errors for countries not allowing deletion
@@ -57,6 +63,12 @@
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertTrue(clearance_date is not None)
+ bank_transaction.reload()
+ bank_transaction.cancel()
+
+ clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
+ self.assertFalse(clearance_date)
+
# 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"))
diff --git a/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py
index 95a5bc3..8b809fe 100644
--- a/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py
+++ b/erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class BankTransactionMapping(Document):
pass
diff --git a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
index d6d7c10..2546f27 100644
--- a/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
+++ b/erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class BankTransactionPayments(Document):
pass
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index d93b6ff..9c20e82 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -1,15 +1,20 @@
- # -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowdate
-from frappe.model.naming import make_autoname
-from erpnext.accounts.utils import get_fiscal_year
from frappe.model.document import Document
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+from frappe.model.naming import make_autoname
+from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
+from erpnext.accounts.utils import get_fiscal_year
+
class BudgetError(frappe.ValidationError): pass
class DuplicateBudgetError(frappe.ValidationError): pass
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 6c25f00..cc82209 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -3,13 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, now_datetime
+
+import frappe
+from frappe.utils import now_datetime, nowdate
+
+from erpnext.accounts.doctype.budget.budget import BudgetError, get_actual_expense
+from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
-from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
test_dependencies = ['Monthly Distribution']
diff --git a/erpnext/accounts/doctype/budget_account/budget_account.py b/erpnext/accounts/doctype/budget_account/budget_account.py
index 81b2709..454e47d 100644
--- a/erpnext/accounts/doctype/budget_account/budget_account.py
+++ b/erpnext/accounts/doctype/budget_account/budget_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class BudgetAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py
index cfe28f3..b1ab648 100644
--- a/erpnext/accounts/doctype/c_form/c_form.py
+++ b/erpnext/accounts/doctype/c_form/c_form.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt
from frappe import _
from frappe.model.document import Document
+from frappe.utils import flt
+
class CForm(Document):
def validate(self):
diff --git a/erpnext/accounts/doctype/c_form/test_c_form.py b/erpnext/accounts/doctype/c_form/test_c_form.py
index c4c95db..e5c5615 100644
--- a/erpnext/accounts/doctype/c_form/test_c_form.py
+++ b/erpnext/accounts/doctype/c_form/test_c_form.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('C-Form')
diff --git a/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py b/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py
index ee5098b..1316227 100644
--- a/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py
+++ b/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CFormInvoiceDetail(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/campaign_item/__init__.py
similarity index 100%
rename from erpnext/accounts/print_format/gst_e_invoice/__init__.py
rename to erpnext/accounts/doctype/campaign_item/__init__.py
diff --git a/erpnext/accounts/doctype/campaign_item/campaign_item.json b/erpnext/accounts/doctype/campaign_item/campaign_item.json
new file mode 100644
index 0000000..69383a4
--- /dev/null
+++ b/erpnext/accounts/doctype/campaign_item/campaign_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-06 16:18:25.410476",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "campaign"
+ ],
+ "fields": [
+ {
+ "fieldname": "campaign",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Campaign",
+ "options": "Campaign"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 10:43:49.717633",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Campaign Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/campaign_item/campaign_item.py b/erpnext/accounts/doctype/campaign_item/campaign_item.py
new file mode 100644
index 0000000..d78fdf5
--- /dev/null
+++ b/erpnext/accounts/doctype/campaign_item/campaign_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CampaignItem(Document):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
index 7251533..96920b3 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js
deleted file mode 100644
index 12ca254..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapper", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Cash Flow Mapper
- () => frappe.tests.make('Cash Flow Mapper', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 28d84b4..9ec466a 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 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
@@ -18,5 +19,3 @@
frappe._('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.js b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js
deleted file mode 100644
index 1970ca8..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapping", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Cash Flow Mapping
- () => frappe.tests.make('Cash Flow Mapping', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 499c820..5e44c61 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
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
class TestCashFlowMapping(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py b/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py
index fc63b8f..5174035 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py
index 6f77a39..fabf579 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js
deleted file mode 100644
index 12546ce..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapping Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Cash Flow Mapping Template
- () => frappe.tests.make('Cash Flow Mapping Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py
index e10b638..f0ff33f 100644
--- a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js
deleted file mode 100644
index eecabda..0000000
--- a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cash Flow Mapping Template Details", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Cash Flow Mapping Template Details
- () => frappe.tests.make('Cash Flow Mapping Template Details', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
index 7ad1d3a..cab7d40 100644
--- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
+++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py
@@ -3,10 +3,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _
from frappe.model.document import Document
-from frappe.utils import cint, flt, cstr
-from frappe import _, msgprint, throw
+from frappe.utils import flt
+
class CashierClosing(Document):
def validate(self):
@@ -33,4 +35,4 @@
def validate_time(self):
if self.from_time >= self.time:
- frappe.throw(_("From Time Should Be Less Than To Time"))
\ No newline at end of file
+ frappe.throw(_("From Time Should Be Less Than To Time"))
diff --git a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.js b/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.js
deleted file mode 100644
index a7fcc8d..0000000
--- a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Cashier Closing", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Cashier Closing
- () => frappe.tests.make('Cashier Closing', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py
index 3c489a7..981093f 100644
--- a/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py
+++ b/erpnext/accounts/doctype/cashier_closing/test_cashier_closing.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCashierClosing(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.py b/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.py
index f737031..a2a8b94 100644
--- a/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.py
+++ b/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.py
@@ -3,8 +3,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CashierClosingPayments(Document):
pass
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
index f795dfa..d61f8a6 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
@@ -10,13 +10,17 @@
// make company mandatory
frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1);
frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
+
+ if (frm.doc.import_file) {
+ frappe.run_serially([
+ () => generate_tree_preview(frm),
+ () => create_import_button(frm),
+ () => frm.set_df_property('chart_preview', 'hidden', 0)
+ ]);
+ }
+
frm.set_df_property('chart_preview', 'hidden',
$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
-
- // Show import button when file is successfully attached
- if (frm.page && frm.page.show_import_button) {
- create_import_button(frm);
- }
},
download_template: function(frm) {
@@ -77,9 +81,6 @@
if (!frm.doc.import_file) {
frm.page.set_indicator("");
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
- } else {
- generate_tree_preview(frm);
- validate_csv_data(frm);
}
},
@@ -104,26 +105,9 @@
}
});
-var validate_csv_data = function(frm) {
- frappe.call({
- method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_accounts",
- args: {file_name: frm.doc.import_file},
- callback: function(r) {
- if(r.message && r.message[0]===true) {
- frm.page["show_import_button"] = true;
- frm.page["total_accounts"] = r.message[1];
- frm.trigger("refresh");
- } else {
- frm.page.set_indicator(__('Resolve error and upload again.'), 'orange');
- frappe.throw(__(r.message));
- }
- }
- });
-};
-
var create_import_button = function(frm) {
frm.page.set_primary_action(__("Import"), function () {
- frappe.call({
+ return frappe.call({
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
args: {
file_name: frm.doc.import_file,
@@ -132,7 +116,7 @@
freeze: true,
freeze_message: __("Creating Accounts..."),
callback: function(r) {
- if(!r.exc) {
+ if (!r.exc) {
clearInterval(frm.page["interval"]);
frm.page.set_indicator(__('Import Successful'), 'blue');
create_reset_button(frm);
@@ -150,12 +134,33 @@
}).addClass('btn btn-primary');
};
+var validate_coa = function(frm) {
+ if (frm.doc.import_file) {
+ let parent = __('All Accounts');
+ return frappe.call({
+ 'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
+ 'args': {
+ file_name: frm.doc.import_file,
+ parent: parent,
+ doctype: 'Chart of Accounts Importer',
+ file_type: frm.doc.file_type,
+ for_validate: 1
+ },
+ callback: function(r) {
+ if (r.message['show_import_button']) {
+ frm.page['show_import_button'] = Boolean(r.message['show_import_button']);
+ }
+ }
+ });
+ }
+};
+
var generate_tree_preview = function(frm) {
let parent = __('All Accounts');
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
// generate tree structure based on the csv data
- new frappe.ui.Tree({
+ return new frappe.ui.Tree({
parent: $(frm.fields_dict['chart_tree'].wrapper),
label: parent,
expandable: true,
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 8456b49..5e596f8 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
@@ -3,18 +3,41 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import csv
+import os
from functools import reduce
-import frappe, csv, os
+
+import frappe
from frappe import _
-from frappe.utils import cstr, cint
from frappe.model.document import Document
+from frappe.utils import cint, cstr
from frappe.utils.csvutils import UnicodeWriter
-from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts, build_tree_from_json
-from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file
+from frappe.utils.xlsxutils import (
+ read_xls_file_from_attached_file,
+ read_xlsx_file_from_attached_file,
+)
+
+from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
+ build_tree_from_json,
+ create_charts,
+)
+
class ChartofAccountsImporter(Document):
def validate(self):
- validate_accounts(self.import_file)
+ if self.import_file:
+ get_coa('Chart of Accounts Importer', 'All Accounts', file_name=self.import_file, for_validate=1)
+
+def validate_columns(data):
+ if not data:
+ 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.whitelist()
def validate_company(company):
@@ -44,6 +67,7 @@
else:
data = generate_data_from_excel(file_doc, extension)
+ frappe.local.flags.ignore_root_company_validation = True
forest = build_forest(data)
create_charts(company, custom_chart=forest)
@@ -108,7 +132,7 @@
return data
@frappe.whitelist()
-def get_coa(doctype, parent, is_root=False, file_name=None):
+def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
''' called by tree view (to fetch node's children) '''
file_doc, extension = get_file(file_name)
@@ -119,13 +143,21 @@
else:
data = generate_data_from_excel(file_doc, extension)
- forest = build_forest(data)
- accounts = build_tree_from_json("", chart_data=forest) # returns alist of dict in a tree render-able form
+ validate_columns(data)
+ validate_accounts(file_doc, extension)
- # filter out to show data for the selected node only
- accounts = [d for d in accounts if d['parent_account']==parent]
+ if not for_validate:
+ forest = build_forest(data)
+ accounts = build_tree_from_json("", chart_data=forest) # returns a list of dict in a tree render-able form
- return accounts
+ # filter out to show data for the selected node only
+ accounts = [d for d in accounts if d['parent_account']==parent]
+
+ return accounts
+ else:
+ return {
+ 'show_import_button': 1
+ }
def build_forest(data):
'''
@@ -282,10 +314,7 @@
@frappe.whitelist()
-def validate_accounts(file_name):
-
- file_doc, extension = get_file(file_name)
-
+def validate_accounts(file_doc, extension):
if extension == 'csv':
accounts = generate_data_from_csv(file_doc, as_dict=True)
else:
@@ -304,15 +333,10 @@
validate_root(accounts_dict)
- validate_account_types(accounts_dict)
-
return [True, len(accounts)]
def validate_root(accounts):
roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
- if len(roots) < 4:
- frappe.throw(_("Number of root accounts cannot be less than 4"))
-
error_messages = []
for account in roots:
@@ -321,9 +345,19 @@
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")))
+ 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)
+
+ missing = list(set(get_root_types()) - root_types_added)
+
+ if missing:
+ frappe.throw(_("Please add Root Account for - {0}").format(' , '.join(missing)))
+
def get_root_types():
return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
@@ -349,23 +383,6 @@
{'account_type': 'Stock', 'root_type': 'Asset'}
]
-
-def validate_account_types(accounts):
- account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
- account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
-
- missing = list(set(account_types_for_ledger) - set(account_types))
- if missing:
- frappe.throw(_("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)))
-
- account_types_for_group = ["Bank", "Cash", "Stock"]
- # fix logic bug
- account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] == 1]
-
- missing = list(set(account_types_for_group) - set(account_groups))
- if missing:
- frappe.throw(_("Please identify/create Account (Group) for type - {0}").format(' , '.join(missing)))
-
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)
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.js
deleted file mode 100644
index b075a01..0000000
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Chart of Accounts Importer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Chart of Accounts Importer
- () => frappe.tests.make('Chart of Accounts Importer', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.py
index 6ab19b7..ca9cf69 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/test_chart_of_accounts_importer.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestChartofAccountsImporter(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js
index 6a430eb..d10c618 100644
--- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js
+++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js
@@ -10,10 +10,10 @@
function() {
erpnext.cheque_print.view_cheque_print(frm);
}).addClass("btn-primary");
-
+
$(frm.fields_dict.cheque_print_preview.wrapper).empty()
-
-
+
+
var template = '<div style="position: relative; overflow-x: scroll;">\
<div id="cheque_preview" style="width: {{ cheque_width }}cm; \
height: {{ cheque_height }}cm;\
@@ -47,9 +47,9 @@
position: absolute;"> Signatory Name </span>\
</div>\
</div>';
-
+
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper)
-
+
if (frm.doc.scanned_cheque) {
$(frm.fields_dict.cheque_print_preview.wrapper).find("#cheque_preview").css('background-image', 'url(' + frm.doc.scanned_cheque + ')');
}
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 d62ee9d..0f595ba 100644
--- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
+++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
class ChequePrintTemplate(Document):
pass
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 fa9c5b5..8ce8794 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
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Cheque Print Template')
diff --git a/erpnext/accounts/doctype/closed_document/closed_document.py b/erpnext/accounts/doctype/closed_document/closed_document.py
index 048ceee..50469bc 100644
--- a/erpnext/accounts/doctype/closed_document/closed_document.py
+++ b/erpnext/accounts/doctype/closed_document/closed_document.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ClosedDocument(Document):
pass
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py
index 8a5473f..166ebb8 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cint, cstr
+from frappe.utils import cint
from frappe.utils.nestedset import NestedSet
+
from erpnext.accounts.utils import validate_field_number
@@ -129,4 +131,4 @@
def check_if_distributed_cost_center_enabled(cost_center_list):
value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
- return next((True for x in value_list if x[0]), False)
\ No newline at end of file
+ return next((True for x in value_list if x[0]), False)
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
index 788ac8b..24cf3ea 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
+++ b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py
@@ -12,4 +12,4 @@
'items': ['Budget Variance Report', 'General Ledger']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js
index fde4123..1d482c5 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js
@@ -51,4 +51,4 @@
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py
index b5fc7e3..142b925 100644
--- a/erpnext/accounts/doctype/cost_center/test_cost_center.py
+++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py
@@ -1,7 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
test_records = frappe.get_test_records('Cost Center')
@@ -62,6 +64,3 @@
cc.is_group = args.is_group or 0
cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
cc.insert()
-
-
-
diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py
index 55c1193..bb2615b 100644
--- a/erpnext/accounts/doctype/coupon_code/coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py
@@ -3,10 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import (strip)
+from frappe.utils import strip
+
+
class CouponCode(Document):
def autoname(self):
self.coupon_name = strip(self.coupon_name)
@@ -17,7 +20,7 @@
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()
-
+
def validate(self):
if self.coupon_type == "Gift Card":
self.maximum_use = 1
diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.js b/erpnext/accounts/doctype/coupon_code/test_coupon_code.js
deleted file mode 100644
index 460fedc..0000000
--- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Coupon Code", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Coupon Code
- () => frappe.tests.make('Coupon Code', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
index 622bd33..bf8c014 100644
--- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py
@@ -3,11 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.stock.get_item_details import get_item_details
-from frappe.test_runner import make_test_objects
test_dependencies = ['Item']
@@ -57,7 +57,7 @@
})
item_price.insert()
# create test item pricing rule
- if not frappe.db.exists("Pricing Rule","_Test Pricing Rule for _Test Item"):
+ 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",
@@ -86,14 +86,15 @@
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": "_Test Pricing Rule for _Test Item",
- "valid_from": "2014-01-01",
- "maximum_use":1,
- "used":0
+ "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()
@@ -102,7 +103,7 @@
test_create_test_data()
def tearDown(self):
- frappe.set_user("Administrator")
+ frappe.set_user("Administrator")
def test_sales_order_with_coupon_code(self):
frappe.db.set_value("Coupon Code", "SAVE30", "used", 0)
@@ -123,6 +124,3 @@
so.submit()
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
-
-
-
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/customer_group_item/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/customer_group_item/__init__.py
diff --git a/erpnext/accounts/doctype/customer_group_item/customer_group_item.json b/erpnext/accounts/doctype/customer_group_item/customer_group_item.json
new file mode 100644
index 0000000..bd1229d
--- /dev/null
+++ b/erpnext/accounts/doctype/customer_group_item/customer_group_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-06 16:12:42.558878",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "customer_group"
+ ],
+ "fields": [
+ {
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Customer Group",
+ "options": "Customer Group"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 10:39:21.563506",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Customer Group Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/customer_group_item/customer_group_item.py b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py
new file mode 100644
index 0000000..100bfd5
--- /dev/null
+++ b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CustomerGroupItem(Document):
+ pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/customer_item/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/customer_item/__init__.py
diff --git a/erpnext/accounts/doctype/customer_item/customer_item.json b/erpnext/accounts/doctype/customer_item/customer_item.json
new file mode 100644
index 0000000..f3dac02
--- /dev/null
+++ b/erpnext/accounts/doctype/customer_item/customer_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-05 14:04:54.266353",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "customer"
+ ],
+ "fields": [
+ {
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Customer ",
+ "options": "Customer"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-06 10:02:32.967841",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Customer Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/customer_item/customer_item.py b/erpnext/accounts/doctype/customer_item/customer_item.py
new file mode 100644
index 0000000..da3533f
--- /dev/null
+++ b/erpnext/accounts/doctype/customer_item/customer_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CustomerItem(Document):
+ pass
diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
index 109737f..11faa77 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class DiscountedInvoice(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
index 48c589f..4367040 100644
--- a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
+++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class DistributedCostCenter(Document):
pass
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index 1ef512a..65ada53 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -3,13 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
+from frappe.utils import cint, flt, getdate
from six import string_types
-from frappe.utils import getdate, get_datetime, rounded, flt, cint
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext.controllers.accounts_controller import AccountsController
diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
index 19a73dd..fa3330f 100644
--- a/erpnext/accounts/doctype/dunning/dunning_dashboard.py
+++ b/erpnext/accounts/doctype/dunning/dunning_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'dunning',
@@ -14,4 +16,4 @@
'items': ['Payment Entry', 'Journal Entry']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index 7fc2e4b..9e8b655 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -3,13 +3,19 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import add_days, today, nowdate
-from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice_against_cost_center
+
+import frappe
+from frappe.utils import add_days, nowdate, today
+
from erpnext.accounts.doctype.dunning.dunning import calculate_interest_and_amount
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ unlink_payment_on_cancel_of_invoice,
+)
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
+ create_sales_invoice_against_cost_center,
+)
class TestDunning(unittest.TestCase):
@@ -143,4 +149,4 @@
'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
}
)
- dunning_type.save()
+ dunning_type.save()
diff --git a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py
index 426497b..b14fdc1 100644
--- a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py
+++ b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class DunningLetterText(Document):
pass
diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.py b/erpnext/accounts/doctype/dunning_type/dunning_type.py
index 8708748..64e7cf4 100644
--- a/erpnext/accounts/doctype/dunning_type/dunning_type.py
+++ b/erpnext/accounts/doctype/dunning_type/dunning_type.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class DunningType(Document):
pass
diff --git a/erpnext/accounts/doctype/dunning_type/test_dunning_type.py b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py
index b2fb26f..ae08907 100644
--- a/erpnext/accounts/doctype/dunning_type/test_dunning_type.py
+++ b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestDunningType(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
index b7b6020..926a442 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js
@@ -31,7 +31,7 @@
}, __('Create'));
}
}
- });
+ });
}
},
@@ -128,4 +128,4 @@
frm.events.get_total_gain_loss(frm);
}
});
-};
\ No newline at end of file
+};
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 f2b0a8c..9c173d0 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -3,13 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import flt
from frappe.model.document import Document
from frappe.model.meta import get_field_precision
-from erpnext.setup.utils import get_exchange_rate
+from frappe.utils import flt
+
+import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_balance_on
+from erpnext.setup.utils import get_exchange_rate
+
class ExchangeRateRevaluation(Document):
def validate(self):
@@ -44,7 +48,7 @@
if total_amt != total_debit:
return True
-
+
return False
@frappe.whitelist()
@@ -205,4 +209,4 @@
"new_balance_in_base_currency": new_balance_in_base_currency
}
- return account_details
\ No newline at end of file
+ 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 b5cfa04..7358f56 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,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'reference_name',
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.js
deleted file mode 100644
index 57c6a78..0000000
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Exchange Rate Revaluation", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Exchange Rate Revaluation
- () => frappe.tests.make('Exchange Rate Revaluation', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
index 3b037d1..e725ce4 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestExchangeRateRevaluation(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.py b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.py
index 87d7b67..58375dd 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation_account/exchange_rate_revaluation_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ExchangeRateRevaluationAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/finance_book/finance_book.py b/erpnext/accounts/doctype/finance_book/finance_book.py
index bc9fce2..527b8e6 100644
--- a/erpnext/accounts/doctype/finance_book/finance_book.py
+++ b/erpnext/accounts/doctype/finance_book/finance_book.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class FinanceBook(Document):
pass
diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.js b/erpnext/accounts/doctype/finance_book/test_finance_book.js
deleted file mode 100644
index 9fb7d4f..0000000
--- a/erpnext/accounts/doctype/finance_book/test_finance_book.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Finance Book", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Finance Book
- () => frappe.tests.make('Finance Book', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py
index 5027658..87a8ae2 100644
--- a/erpnext/accounts/doctype/finance_book/test_finance_book.py
+++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py
@@ -2,26 +2,17 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
+import unittest
+
+import frappe
+
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
-import frappe
-import unittest
-
class TestFinanceBook(unittest.TestCase):
- def create_finance_book(self):
- 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()
- else:
- finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
-
- return finance_book
-
def test_finance_book(self):
- finance_book = self.create_finance_book()
+ finance_book = create_finance_book()
# create jv entry
jv = make_journal_entry("_Test Bank - _TC",
@@ -41,3 +32,14 @@
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()
+ else:
+ finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
+
+ return finance_book
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index 4255626..6854ac9 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -2,12 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import msgprint, _
-from frappe.utils import getdate, add_days, add_years, cstr
-from dateutil.relativedelta import relativedelta
+import frappe
+from dateutil.relativedelta import relativedelta
+from frappe import _, msgprint
from frappe.model.document import Document
+from frappe.utils import add_days, add_years, cstr, getdate
+
class FiscalYearIncorrectDate(frappe.ValidationError): pass
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
index 58480df..92e8a42 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year_dashboard.py
@@ -13,7 +13,7 @@
},
{
'label': _('References'),
- 'items': ['Period Closing Voucher', 'Tax Withholding Category']
+ 'items': ['Period Closing Voucher']
},
{
'label': _('Target Details'),
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index cec4f44..b0365af 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -3,7 +3,9 @@
from __future__ import unicode_literals
-import frappe, unittest
+import unittest
+
+import frappe
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
diff --git a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json
index 3eb0d74..67acb26 100644
--- a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json
+++ b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json
@@ -1,63 +1,33 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2014-10-02 13:35:44.155278",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
+ "actions": [],
+ "creation": "2014-10-02 13:35:44.155278",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company"
+ ],
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company"
}
- ],
- "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": "2016-07-11 03:28:00.505946",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Fiscal Year Company",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_seen": 0
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-28 18:01:53.495929",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Fiscal Year Company",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py
index 8dfc2fa..b9c57f6 100644
--- a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py
+++ b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class FiscalYearCompany(Document):
pass
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 0844995..1e983b1 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -2,20 +2,30 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import flt, fmt_money, getdate, formatdate, cint
from frappe.model.document import Document
-from frappe.model.naming import set_name_from_naming_options
from frappe.model.meta import get_field_precision
-from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled
-from erpnext.accounts.utils import get_account_currency
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext.exceptions import InvalidAccountCurrency, InvalidAccountDimensionError, MandatoryAccountDimensionError
-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 frappe.model.naming import set_name_from_naming_options
+from frappe.utils import flt, fmt_money
from six import iteritems
+import erpnext
+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.party import validate_party_frozen_disabled, validate_party_gle_currency
+from erpnext.accounts.utils import get_account_currency, get_fiscal_year
+from erpnext.exceptions import (
+ InvalidAccountCurrency,
+ InvalidAccountDimensionError,
+ MandatoryAccountDimensionError,
+)
+
exclude_from_linked_with = True
class GLEntry(Document):
def autoname(self):
diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.js b/erpnext/accounts/doctype/gl_entry/test_gl_entry.js
deleted file mode 100644
index 2986e5e..0000000
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GL Entry", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('GL Entry', [
- // insert a new GL Entry
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
index 4167ca7..1495952 100644
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
@@ -2,10 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, unittest
+
+import unittest
+
+import frappe
from frappe.model.naming import parse_naming_series
-from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+
from erpnext.accounts.doctype.gl_entry.gl_entry import rename_gle_sle_docs
+from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+
class TestGLEntry(unittest.TestCase):
def test_round_off_entry(self):
diff --git a/erpnext/accounts/doctype/gst_account/gst_account.py b/erpnext/accounts/doctype/gst_account/gst_account.py
index d784849..9ca3f9a 100644
--- a/erpnext/accounts/doctype/gst_account/gst_account.py
+++ b/erpnext/accounts/doctype/gst_account/gst_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class GSTAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index b73d8bf..8867f1c 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -3,12 +3,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json, erpnext
+
+import json
+
+import frappe
from frappe import _
-from frappe.utils import flt, getdate, nowdate, add_days
-from erpnext.controllers.accounts_controller import AccountsController
+from frappe.utils import add_days, flt, getdate, nowdate
+
+import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
from erpnext.accounts.general_ledger import make_gl_entries
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+from erpnext.controllers.accounts_controller import AccountsController
+
class InvoiceDiscounting(AccountsController):
def validate(self):
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
index 6523cd3..bab8e46 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'reference_name',
@@ -17,4 +19,4 @@
'items': ['Payment Entry', 'Journal Entry']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js
index a72023d..4895efc 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js
@@ -18,4 +18,4 @@
return [__("Canceled"), "red", "status,=,Canceled"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
index 919dd0c..58aea92 100644
--- a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
@@ -3,13 +3,17 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import nowdate, add_days, flt
import unittest
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
+
+import frappe
+from frappe.utils import add_days, flt, nowdate
+
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry_against_invoice
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
+
+
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")
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 d9155cb..1e26afe 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
class ItemTaxTemplate(Document):
def validate(self):
diff --git a/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.js
deleted file mode 100644
index 6893499..0000000
--- a/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Tax Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Item Tax Template
- () => frappe.tests.make('Item Tax Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.py b/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.py
index acf1e44..46bb300 100644
--- a/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.py
+++ b/erpnext/accounts/doctype/item_tax_template/test_item_tax_template.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestItemTaxTemplate(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py b/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py
index d51bed0..aa3b542 100644
--- a/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py
+++ b/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ItemTaxTemplateDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index b7bbb74..20678d7 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -13,10 +13,12 @@
"voucher_type",
"naming_series",
"finance_book",
+ "tax_withholding_category",
"column_break1",
"from_template",
"company",
"posting_date",
+ "apply_tds",
"2_add_edit_gl_entries",
"accounts",
"section_break99",
@@ -498,16 +500,32 @@
"options": "Journal Entry Template",
"print_hide": 1,
"report_hide": 1
+ },
+ {
+ "depends_on": "eval:doc.apply_tds",
+ "fieldname": "tax_withholding_category",
+ "fieldtype": "Link",
+ "label": "Tax Withholding Category",
+ "mandatory_depends_on": "eval:doc.apply_tds",
+ "options": "Tax Withholding Category"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:['Credit Note', 'Debit Note'].includes(doc.voucher_type)",
+ "fieldname": "apply_tds",
+ "fieldtype": "Check",
+ "label": "Apply Tax Withholding Amount "
}
],
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-30 13:56:01.121995",
+ "modified": "2021-09-09 15:31:14.484029",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 937597b..e568a82 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -2,19 +2,33 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext, json
-from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, nowdate, cint, get_link_to_form
-from frappe import msgprint, _, scrub
-from erpnext.controllers.accounts_controller import AccountsController
-from erpnext.accounts.utils import get_balance_on, get_stock_accounts, get_stock_and_account_balance, \
- get_account_currency, check_if_stock_and_account_balance_synced
-from erpnext.accounts.party import get_party_account
-from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
-from erpnext.accounts.doctype.invoice_discounting.invoice_discounting \
- import get_party_account_based_on_invoice_discounting
-from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
-from six import string_types, iteritems
+import json
+
+import frappe
+from frappe import _, msgprint, scrub
+from frappe.utils import cint, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
+from six import iteritems, string_types
+
+import erpnext
+from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
+from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
+ get_party_account_based_on_invoice_discounting,
+)
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
+ get_party_tax_withholding_details,
+)
+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,
+ get_stock_and_account_balance,
+)
+from erpnext.controllers.accounts_controller import AccountsController
+from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
+
class StockAccountInvalidTransaction(frappe.ValidationError): pass
@@ -46,7 +60,8 @@
self.validate_against_jv()
self.validate_reference_doc()
- self.set_against_account()
+ if self.docstatus == 0:
+ self.set_against_account()
self.create_remarks()
self.set_print_format_fields()
self.validate_expense_claim()
@@ -55,6 +70,10 @@
self.set_account_and_party_balance()
self.validate_inter_company_accounts()
self.validate_stock_accounts()
+
+ if self.docstatus == 0:
+ self.apply_tax_withholding()
+
if not self.title:
self.title = self.get_title()
@@ -66,6 +85,7 @@
self.update_expense_claim()
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)
@@ -83,6 +103,7 @@
self.unlink_inter_company_jv()
self.unlink_asset_adjustment_entry()
self.update_invoice_discounting()
+ self.update_status_for_full_and_final_statement()
def get_title(self):
return self.pay_to_recd_from or self.accounts[0].account
@@ -98,6 +119,15 @@
for voucher_no in list(set(order_list)):
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
+ def update_status_for_full_and_final_statement(self):
+ for entry in self.accounts:
+ if entry.reference_type == "Full and Final Statement":
+ if self.docstatus == 1:
+ frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Paid")
+ 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:
doc = frappe.get_doc("Journal Entry", self.inter_company_journal_entry_reference)
@@ -117,6 +147,72 @@
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'):
+ return
+
+ 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_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'))
+
+ 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
+ })
+
+ tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
+
+ if not tax_withholding_details:
+ 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')
+ })
+
+ 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 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]
+ })
+
+ 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,\
@@ -643,7 +739,10 @@
for d in self.accounts:
if d.reference_type=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
- update_reimbursed_amount(doc, jv=self.name)
+ if self.docstatus == 2:
+ update_reimbursed_amount(doc, -1 * d.debit)
+ else:
+ update_reimbursed_amount(doc, d.debit)
def validate_expense_claim(self):
diff --git a/erpnext/accounts/doctype/journal_entry/regional/india.js b/erpnext/accounts/doctype/journal_entry/regional/india.js
index 75a69ac..c5f5520 100644
--- a/erpnext/accounts/doctype/journal_entry/regional/india.js
+++ b/erpnext/accounts/doctype/journal_entry/regional/india.js
@@ -14,4 +14,4 @@
};
});
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index 5f003e0..d03a088 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -2,11 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import unittest, frappe
+
+import unittest
+
+import frappe
from frappe.utils import flt, nowdate
+
from erpnext.accounts.doctype.account.test_account import get_inventory_account
-from erpnext.exceptions import InvalidAccountCurrency
from erpnext.accounts.doctype.journal_entry.journal_entry import StockAccountInvalidTransaction
+from erpnext.exceptions import InvalidAccountCurrency
+
class TestJournalEntry(unittest.TestCase):
def test_journal_entry_with_against_jv(self):
@@ -100,7 +105,7 @@
"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",
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index a89fefd..dff883a 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -202,7 +202,7 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
- "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees"
+ "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
},
{
"fieldname": "reference_name",
@@ -280,7 +280,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-06-26 14:06:54.833738",
+ "modified": "2021-08-30 21:27:32.200299",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py
index 2e77cf2..86d3df4 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class JournalEntryAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
index cbb9fc4..1c19c1d 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
@@ -88,4 +88,4 @@
frappe.model.clear_table(frm.doc, "accounts");
frm.refresh_field("accounts");
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
index e0b9cbc..f0813f5 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class JournalEntryTemplate(Document):
pass
diff --git a/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py
index 5f74a20..61fea94 100644
--- a/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py
+++ b/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestJournalEntryTemplate(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py
index 48e6abb..d0408ca 100644
--- a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py
+++ b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class JournalEntryTemplateAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
index 3579a1a..0813926 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
from frappe.utils import today
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js
deleted file mode 100644
index a916b67..0000000
--- a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Loyalty Point Entry", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Loyalty Point Entry
- () => frappe.tests.make('Loyalty Point Entry', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py
index b6e2d57..07856cf 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/test_loyalty_point_entry.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestLoyaltyPointEntry(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py b/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py
index e4382b6..506e2ce 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry_redemption/loyalty_point_entry_redemption.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LoyaltyPointEntryRedemption(Document):
pass
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
index cb753a3..89ed461 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py
@@ -3,11 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import today, flt
+from frappe.utils import flt, today
+
class LoyaltyProgram(Document):
pass
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
index 189004f..267bbbf 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program_dashboard.py
@@ -1,7 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js
deleted file mode 100644
index 9321c14..0000000
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Loyalty Program", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Loyalty Program
- () => frappe.tests.make('Loyalty Program', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
index 3199488..a039e32 100644
--- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
+++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py
@@ -3,12 +3,17 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import today, cint, flt, getdate
-from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
+
+import frappe
+from frappe.utils import cint, flt, getdate, today
+
+from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
+ get_loyalty_program_details_with_points,
+)
from erpnext.accounts.party import get_dashboard_info
+
class TestLoyaltyProgram(unittest.TestCase):
@classmethod
def setUpClass(self):
diff --git a/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
index 42cc38c..4bbcf3a 100644
--- a/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
+++ b/erpnext/accounts/doctype/loyalty_program_collection/loyalty_program_collection.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LoyaltyProgramCollection(Document):
pass
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
index 7a06d35..103fa96 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
@@ -14,4 +14,4 @@
};
});
},
-});
\ No newline at end of file
+});
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 3247369..dfe42df 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
+import frappe
from frappe import _
+from frappe.model.document import Document
+
class ModeofPayment(Document):
def validate(self):
@@ -39,4 +40,3 @@
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")
-
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 ad6cd47..299687b 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
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Mode of Payment')
diff --git a/erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.py b/erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.py
index 5cb195a..40b5f30 100644
--- a/erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.py
+++ b/erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ModeofPaymentAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
index bff6422..c0e00d6 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import (flt, add_months)
from frappe.model.document import Document
+from frappe.utils import add_months, flt
+
class MonthlyDistribution(Document):
@frappe.whitelist()
@@ -55,4 +57,4 @@
if d.month in months:
percentage += d.percentage_allocation
- return percentage
\ No newline at end of file
+ return percentage
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
index a679499..912bd9e 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py
@@ -20,4 +20,4 @@
'items': ['Budget']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
index efbf4eb..63faa15 100644
--- a/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/test_monthly_distribution.py
@@ -2,9 +2,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
+import frappe
+
test_records = frappe.get_test_records('Monthly Distribution')
class TestMonthlyDistribution(unittest.TestCase):
diff --git a/erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.py b/erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.py
index d9e8bf9..8ec30c7 100644
--- a/erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.py
+++ b/erpnext/accounts/doctype/monthly_distribution_percentage/monthly_distribution_percentage.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MonthlyDistributionPercentage(Document):
pass
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js
index a8c07d6..7eb5c42 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js
@@ -162,4 +162,4 @@
invoices_add: (frm) => {
frm.trigger('update_invoice_table');
}
-});
\ No newline at end of file
+});
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 d76d909..2b94bf4 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
@@ -3,14 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import traceback
from json import dumps
+
+import frappe
from frappe import _, scrub
-from frappe.utils import flt, nowdate
from frappe.model.document import Document
+from frappe.utils import flt, nowdate
from frappe.utils.background_jobs import enqueue
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
class OpeningInvoiceCreationTool(Document):
@@ -241,4 +246,3 @@
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
return accounts[0].name
-
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.js
deleted file mode 100644
index f95d0d8..0000000
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Opening Invoice Creation Tool", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Opening Invoice Creation Tool
- () => frappe.tests.make('Opening Invoice Creation Tool', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 8d6de2d..ed3c6a9 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
@@ -3,12 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
from frappe.cache_manager import clear_doctype_cache
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
+
+from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import (
+ get_temporary_opening_account,
+)
test_dependencies = ["Customer", "Supplier"]
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.py b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.py
index d47c3e9..4008022 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class OpeningInvoiceCreationToolItem(Document):
pass
diff --git a/erpnext/accounts/doctype/party_account/party_account.py b/erpnext/accounts/doctype/party_account/party_account.py
index 21cfb96..08d67c7 100644
--- a/erpnext/accounts/doctype/party_account/party_account.py
+++ b/erpnext/accounts/doctype/party_account/party_account.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PartyAccount(Document):
pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/party_link/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/party_link/__init__.py
diff --git a/erpnext/accounts/doctype/party_link/party_link.js b/erpnext/accounts/doctype/party_link/party_link.js
new file mode 100644
index 0000000..6da9291
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.js
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Party Link', {
+ refresh: function(frm) {
+ frm.set_query('primary_role', () => {
+ return {
+ filters: {
+ name: ['in', ['Customer', 'Supplier']]
+ }
+ };
+ });
+
+ frm.set_query('secondary_role', () => {
+ let party_types = Object.keys(frappe.boot.party_account_types)
+ .filter(p => p != frm.doc.primary_role);
+ return {
+ filters: {
+ name: ['in', party_types]
+ }
+ };
+ });
+ },
+
+ primary_role(frm) {
+ frm.set_value('primary_party', '');
+ frm.set_value('secondary_role', '');
+ },
+
+ secondary_role(frm) {
+ frm.set_value('secondary_party', '');
+ }
+});
diff --git a/erpnext/accounts/doctype/party_link/party_link.json b/erpnext/accounts/doctype/party_link/party_link.json
new file mode 100644
index 0000000..a1bb15f
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.json
@@ -0,0 +1,102 @@
+{
+ "actions": [],
+ "autoname": "ACC-PT-LNK-.###.",
+ "creation": "2021-08-18 21:06:53.027695",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "primary_role",
+ "secondary_role",
+ "column_break_2",
+ "primary_party",
+ "secondary_party"
+ ],
+ "fields": [
+ {
+ "fieldname": "primary_role",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Primary Role",
+ "options": "DocType",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "primary_role",
+ "fieldname": "secondary_role",
+ "fieldtype": "Link",
+ "label": "Secondary Role",
+ "mandatory_depends_on": "primary_role",
+ "options": "DocType"
+ },
+ {
+ "depends_on": "primary_role",
+ "fieldname": "primary_party",
+ "fieldtype": "Dynamic Link",
+ "label": "Primary Party",
+ "mandatory_depends_on": "primary_role",
+ "options": "primary_role"
+ },
+ {
+ "depends_on": "secondary_role",
+ "fieldname": "secondary_party",
+ "fieldtype": "Dynamic Link",
+ "label": "Secondary Party",
+ "mandatory_depends_on": "secondary_role",
+ "options": "secondary_role"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-08-25 20:08:56.761150",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Party Link",
+ "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,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "primary_party",
+ "track_changes": 1
+}
\ 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
new file mode 100644
index 0000000..daf667c
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/party_link.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2021, 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 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"))
+
+ 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]))
+
+ 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]))
diff --git a/erpnext/accounts/doctype/party_link/test_party_link.py b/erpnext/accounts/doctype/party_link/test_party_link.py
new file mode 100644
index 0000000..2ae3381
--- /dev/null
+++ b/erpnext/accounts/doctype/party_link/test_party_link.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestPartyLink(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 439b1ed..3be3925 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -533,8 +533,8 @@
source_exchange_rate: function(frm) {
if (frm.doc.paid_amount) {
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
- if(!frm.set_paid_amount_based_on_received_amount &&
- (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
+ // target exchange rate should always be same as source if both account currencies is same
+ if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
}
@@ -872,7 +872,7 @@
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
- + frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
+ - frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 46904f7..8037ca1 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -3,22 +3,37 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext, json
-from frappe import _, scrub, ValidationError, throw
-from frappe.utils import flt, comma_or, nowdate, getdate, cint
-from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
-from erpnext.accounts.party import get_party_account
-from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
-from erpnext.setup.utils import get_exchange_rate
-from erpnext.accounts.general_ledger import make_gl_entries
-from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
-from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details
-from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
-from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
-from six import string_types, iteritems
-from erpnext.controllers.accounts_controller import validate_taxes_and_charges
+import json
+
+import frappe
+from frappe import ValidationError, _, scrub, throw
+from frappe.utils import cint, comma_or, flt, getdate, nowdate
+from six import iteritems, string_types
+
+import erpnext
+from erpnext.accounts.doctype.bank_account.bank_account import (
+ get_bank_account_details,
+ get_party_bank_account,
+)
+from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
+ get_party_account_based_on_invoice_discounting,
+)
+from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
+ get_party_tax_withholding_details,
+)
+from erpnext.accounts.general_ledger import make_gl_entries
+from erpnext.accounts.party import get_party_account
+from erpnext.accounts.utils import get_account_currency, get_balance_on, get_outstanding_invoices
+from erpnext.controllers.accounts_controller import (
+ AccountsController,
+ get_supplier_block_status,
+ validate_taxes_and_charges,
+)
+from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
+from erpnext.setup.utils import get_exchange_rate
+
class InvalidPaymentEntry(ValidationError):
pass
@@ -55,14 +70,17 @@
self.validate_mandatory()
self.validate_reference_documents()
self.set_tax_withholding()
- self.apply_taxes()
self.set_amounts()
+ self.validate_amounts()
+ self.apply_taxes()
+ self.set_amounts_after_tax()
self.clear_unallocated_reference_document_rows()
self.validate_payment_against_negative_invoice()
self.validate_transaction_reference()
self.set_title()
self.set_remarks()
self.validate_duplicate_entry()
+ self.validate_payment_type_with_outstanding()
self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
@@ -72,9 +90,9 @@
if self.difference_amount:
frappe.throw(_("Difference Amount must be zero"))
self.make_gl_entries()
+ self.update_expense_claim()
self.update_outstanding_amounts()
self.update_advance_paid()
- self.update_expense_claim()
self.update_donation()
self.update_payment_schedule()
self.set_status()
@@ -82,9 +100,9 @@
def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.make_gl_entries(cancel=1)
+ self.update_expense_claim()
self.update_outstanding_amounts()
self.update_advance_paid()
- self.update_expense_claim()
self.update_donation(cancel=1)
self.delink_advance_entry_references()
self.update_payment_schedule(cancel=1)
@@ -118,6 +136,11 @@
if not self.get(field):
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"))
+
def validate_allocated_amount(self):
for d in self.get("references"):
if (flt(d.allocated_amount))> 0:
@@ -185,7 +208,7 @@
for field, value in iteritems(ref_details):
if d.exchange_gain_loss:
# for cases where gain/loss is booked into invoice
- # exchange_gain_loss is calculated from invoice & populated
+ # exchange_gain_loss is calculated from invoice & populated
# and row.exchange_rate is already set to payment entry's exchange rate
# refer -> `update_reference_in_payment_entry()` in utils.py
continue
@@ -236,7 +259,9 @@
self.company_currency, self.posting_date)
def set_target_exchange_rate(self, ref_doc=None):
- if self.paid_to and not self.target_exchange_rate:
+ if self.paid_from_account_currency == self.paid_to_account_currency:
+ self.target_exchange_rate = self.source_exchange_rate
+ elif self.paid_to and not self.target_exchange_rate:
if ref_doc:
if self.paid_to_account_currency == ref_doc.currency:
self.target_exchange_rate = ref_doc.get("exchange_rate")
@@ -365,6 +390,9 @@
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
for key, allocated_amount in iteritems(invoice_payment_amount_map):
+ if not invoice_paid_amount_map.get(key):
+ 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'))
@@ -417,7 +445,7 @@
net_total_for_tds = 0
if reference.reference_doctype == 'Purchase Order':
net_total_for_tds += flt(frappe.db.get_value('Purchase Order', reference.reference_name, 'net_total'))
-
+
if net_total_for_tds:
net_total = net_total_for_tds
@@ -468,13 +496,22 @@
def set_amounts(self):
self.set_received_amount()
self.set_amounts_in_company_currency()
- self.set_amounts_after_tax()
self.set_total_allocated_amount()
self.set_unallocated_amount()
self.set_difference_amount()
+ def validate_amounts(self):
+ self.validate_received_amount()
+
+ def validate_received_amount(self):
+ if self.paid_from_account_currency == self.paid_to_account_currency:
+ if self.paid_amount != self.received_amount:
+ frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
+
def set_received_amount(self):
self.base_received_amount = self.base_paid_amount
+ if self.paid_from_account_currency == self.paid_to_account_currency:
+ self.received_amount = self.paid_amount
def set_amounts_after_tax(self):
applicable_tax = 0
@@ -529,7 +566,7 @@
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.received_amount + total_deductions -
+ 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" \
@@ -736,9 +773,11 @@
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':
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
payment_or_advance_account = self.get_party_account_for_taxes()
@@ -760,14 +799,13 @@
"cost_center": d.cost_center
}, account_currency, item=d))
- #Intentionally use -1 to get net values in party account
if not d.included_in_paid_amount or self.advance_tax_account:
gl_entries.append(
self.get_gl_dict({
"account": payment_or_advance_account,
"against": against,
- dr_or_cr: -1 * tax_amount,
- dr_or_cr + "_in_account_currency": -1 * base_tax_amount
+ 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,
@@ -811,7 +849,10 @@
for d in self.get("references"):
if d.reference_doctype=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
- update_reimbursed_amount(doc, self.name)
+ if self.docstatus == 2:
+ update_reimbursed_amount(doc, -1 * d.allocated_amount)
+ else:
+ update_reimbursed_amount(doc, d.allocated_amount)
def update_donation(self, cancel=0):
if self.payment_type == "Receive" and self.party_type == "Donor" and self.party:
@@ -841,7 +882,7 @@
if account_details:
row.update(account_details)
-
+
if not row.get('amount'):
# if no difference amount
return
@@ -1367,7 +1408,7 @@
})
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
+ total_amount = outstanding_amount = exchange_rate = None
if reference_doctype == "Fees":
total_amount = ref_doc.get("grand_total")
exchange_rate = 1
@@ -1387,7 +1428,7 @@
return total_amount, outstanding_amount, exchange_rate
def get_amounts_based_on_ref_doc(reference_doctype, ref_doc, party_account_currency, company_currency):
- total_amount, outstanding_amount, exchange_rate = None
+ 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)
elif ref_doc.doctype == "Employee Advance":
@@ -1427,7 +1468,7 @@
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):
- outstanding_amount, bill_no = None
+ 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")
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js
index e6d83b9..2d76fe6 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js
@@ -11,4 +11,4 @@
};
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/payment_entry/regional/india.js b/erpnext/accounts/doctype/payment_entry/regional/india.js
new file mode 100644
index 0000000..abb3445
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/regional/india.js
@@ -0,0 +1,29 @@
+frappe.ui.form.on("Payment Entry", {
+ company: function(frm) {
+ frappe.call({
+ 'method': 'frappe.contacts.doctype.address.address.get_default_address',
+ 'args': {
+ 'doctype': 'Company',
+ 'name': frm.doc.company
+ },
+ 'callback': function(r) {
+ frm.set_value('company_address', r.message);
+ }
+ });
+ },
+
+ party: function(frm) {
+ if (frm.doc.party_type == "Customer" && frm.doc.party) {
+ frappe.call({
+ 'method': 'frappe.contacts.doctype.address.address.get_default_address',
+ 'args': {
+ 'doctype': 'Customer',
+ 'name': frm.doc.party
+ },
+ 'callback': function(r) {
+ frm.set_value('customer_address', r.message);
+ }
+ });
+ }
+ }
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index d1302f5..c90a3c5 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -3,14 +3,25 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import flt, nowdate
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, InvalidPaymentEntry
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice, create_sales_invoice_against_cost_center
-from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice, make_purchase_invoice_against_cost_center
+
+from erpnext.accounts.doctype.payment_entry.payment_entry import (
+ InvalidPaymentEntry,
+ get_payment_entry,
+)
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ make_purchase_invoice,
+ make_purchase_invoice_against_cost_center,
+)
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
+ create_sales_invoice,
+ create_sales_invoice_against_cost_center,
+)
from erpnext.hr.doctype.expense_claim.test_expense_claim import make_expense_claim
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
test_dependencies = ["Item"]
@@ -107,7 +118,7 @@
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.target_exchange_rate = 50
+ pe.source_exchange_rate = 50
pe.insert()
pe.submit()
@@ -154,7 +165,7 @@
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.target_exchange_rate = 50
+ pe.source_exchange_rate = 50
pe.insert()
pe.submit()
@@ -295,8 +306,39 @@
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)
+
+ 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.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.save()
+
+ self.assertEqual(flt(pe.difference_amount, 2), 0.0)
+ self.assertEqual(flt(pe.unallocated_amount, 2), 0.0)
+
def test_payment_entry_retrieves_last_exchange_rate(self):
- from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
+ from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
+ save_new_records,
+ test_records,
+ )
save_new_records(test_records)
@@ -463,7 +505,7 @@
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.target_exchange_rate = 55
+ pe.source_exchange_rate = 55
pe.append("deductions", {
"account": "_Test Exchange Gain/Loss - _TC",
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js
index 14aa073..e8db2c3 100644
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js
+++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js
@@ -57,4 +57,4 @@
() => frappe.timeout(3),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
index 0c76343..34af79f 100644
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js
@@ -25,4 +25,4 @@
() => frappe.timeout(0.3),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
index 9849d76..8c7f6f4 100644
--- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
+++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js
@@ -64,4 +64,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
index d6686bb..9cfed7b 100644
--- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PaymentEntryDeduction(Document):
pass
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
index 43eb0b6..8961167 100644
--- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
@@ -93,6 +93,7 @@
"options": "Payment Term"
},
{
+ "depends_on": "exchange_gain_loss",
"fieldname": "exchange_gain_loss",
"fieldtype": "Currency",
"label": "Exchange Gain/Loss",
@@ -103,7 +104,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-04-21 13:30:11.605388",
+ "modified": "2021-09-26 17:06:55.597389",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Reference",
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
index 51f8c06..a686f49 100644
--- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PaymentEntryReference(Document):
pass
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 fd213a4..1d8a8ac 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py
@@ -3,25 +3,27 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class PaymentGatewayAccount(Document):
def autoname(self):
self.name = self.payment_gateway + " - " + self.currency
-
+
def validate(self):
self.currency = frappe.db.get_value("Account", self.payment_account, "account_currency")
-
+
self.update_default_payment_gateway()
self.set_as_default_if_not_set()
-
+
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 """)
-
+
def set_as_default_if_not_set(self):
- if not frappe.db.get_value("Payment Gateway Account",
+ 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 0898229..2edc1a1 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,7 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
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 84c3bc4..f76aa4a 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
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Payment Gateway Account')
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js
index d12e474..aa373bc 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.js
+++ b/erpnext/accounts/doctype/payment_order/payment_order.js
@@ -136,4 +136,4 @@
dialog.show();
},
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py
index 8d29ae7..e9b5ad9 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import nowdate
-from erpnext.accounts.party import get_party_account
from frappe.model.document import Document
+from frappe.utils import nowdate
+
+from erpnext.accounts.party import get_party_account
+
class PaymentOrder(Document):
def on_submit(self):
diff --git a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
index 6b93f92..d9262be 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Payment Entry', 'Journal Entry']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.js b/erpnext/accounts/doctype/payment_order/test_payment_order.js
deleted file mode 100644
index f63fc54..0000000
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Order", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Payment Order
- () => frappe.tests.make('Payment Order', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py
index 5fdde07..6414473 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -3,13 +3,19 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import getdate
+
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
-from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, make_payment_order
+from erpnext.accounts.doctype.payment_entry.payment_entry import (
+ get_payment_entry,
+ make_payment_order,
+)
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+
class TestPaymentOrder(unittest.TestCase):
def setUp(self):
create_bank_account()
@@ -46,4 +52,4 @@
doc = make_payment_order(ref_doc.name, payment_order)
doc.save()
doc.submit()
- return doc
\ No newline at end of file
+ return doc
diff --git a/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.py b/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.py
index b3a9294..4bb98a3 100644
--- a/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.py
+++ b/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PaymentOrderReference(Document):
pass
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index c71a62d..412833b 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -2,46 +2,10 @@
// For license information, please see license.txt
frappe.provide("erpnext.accounts");
-
-frappe.ui.form.on("Payment Reconciliation Payment", {
- invoice_number: function(frm, cdt, cdn) {
- var row = locals[cdt][cdn];
- if(row.invoice_number) {
- var parts = row.invoice_number.split(' | ');
- var invoice_type = parts[0];
- var invoice_number = parts[1];
-
- var invoice_amount = frm.doc.invoices.filter(function(d) {
- return d.invoice_type === invoice_type && d.invoice_number === invoice_number;
- })[0].outstanding_amount;
-
- frappe.model.set_value(cdt, cdn, "allocated_amount", Math.min(invoice_amount, row.amount));
-
- frm.call({
- doc: frm.doc,
- method: 'get_difference_amount',
- args: {
- child_row: row
- },
- callback: function(r, rt) {
- if(r.message) {
- frappe.model.set_value(cdt, cdn,
- "difference_amount", r.message);
- }
- }
- });
- }
- }
-});
-
erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller {
onload() {
var me = this;
- this.frm.set_query("party", function() {
- check_mandatory(me.frm);
- });
-
this.frm.set_query("party_type", function() {
return {
"filters": {
@@ -88,15 +52,50 @@
refresh() {
this.frm.disable_save();
- this.toggle_primary_action();
+ this.frm.set_df_property('invoices', 'cannot_delete_rows', true);
+ this.frm.set_df_property('payments', 'cannot_delete_rows', true);
+ this.frm.set_df_property('allocation', 'cannot_delete_rows', true);
+
+ this.frm.set_df_property('invoices', 'cannot_add_rows', true);
+ this.frm.set_df_property('payments', 'cannot_add_rows', true);
+ this.frm.set_df_property('allocation', 'cannot_add_rows', true);
+
+
+ if (this.frm.doc.receivable_payable_account) {
+ this.frm.add_custom_button(__('Get Unreconciled Entries'), () =>
+ this.frm.trigger("get_unreconciled_entries")
+ );
+ this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary');
+ }
+ if (this.frm.doc.invoices.length && this.frm.doc.payments.length) {
+ this.frm.add_custom_button(__('Allocate'), () =>
+ this.frm.trigger("allocate")
+ );
+ this.frm.change_custom_button_type('Allocate', null, 'primary');
+ this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default');
+ }
+ if (this.frm.doc.allocation.length) {
+ this.frm.add_custom_button(__('Reconcile'), () =>
+ this.frm.trigger("reconcile")
+ );
+ this.frm.change_custom_button_type('Reconcile', null, 'primary');
+ this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default');
+ this.frm.change_custom_button_type('Allocate', null, 'default');
+ }
}
- onload_post_render() {
- this.toggle_primary_action();
+ company() {
+ var me = this;
+ this.frm.set_value('receivable_payable_account', '');
+ me.frm.clear_table("allocation");
+ me.frm.clear_table("invoices");
+ me.frm.clear_table("payments");
+ me.frm.refresh_fields();
+ me.frm.trigger('party');
}
party() {
- var me = this
+ var me = this;
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
return frappe.call({
method: "erpnext.accounts.party.get_party_account",
@@ -109,6 +108,7 @@
if (!r.exc && r.message) {
me.frm.set_value("receivable_payable_account", r.message);
}
+ me.frm.refresh();
}
});
}
@@ -120,16 +120,41 @@
doc: me.frm.doc,
method: 'get_unreconciled_entries',
callback: function(r, rt) {
- me.set_invoice_options();
- me.toggle_primary_action();
+ if (!(me.frm.doc.payments.length || me.frm.doc.invoices.length)) {
+ frappe.throw({message: __("No invoice and payment records found for this party")});
+ }
+ me.frm.refresh();
}
});
}
+ allocate() {
+ var me = this;
+ let payments = me.frm.fields_dict.payments.grid.get_selected_children();
+ if (!(payments.length)) {
+ payments = me.frm.doc.payments;
+ }
+ let invoices = me.frm.fields_dict.invoices.grid.get_selected_children();
+ if (!(invoices.length)) {
+ invoices = me.frm.doc.invoices;
+ }
+ return me.frm.call({
+ doc: me.frm.doc,
+ method: 'allocate_entries',
+ args: {
+ payments: payments,
+ invoices: invoices
+ },
+ callback: function() {
+ me.frm.refresh();
+ }
+ });
+ }
+
reconcile() {
var me = this;
- var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
+ var show_dialog = me.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
if (show_dialog && show_dialog.length) {
@@ -138,7 +163,7 @@
title: __("Select Difference Account"),
fields: [
{
- fieldname: "payments", fieldtype: "Table", label: __("Payments"),
+ fieldname: "allocation", fieldtype: "Table", label: __("Allocation"),
data: this.data, in_place_edit: true,
get_data: () => {
return this.data;
@@ -179,10 +204,10 @@
},
],
primary_action: function() {
- const args = dialog.get_values()["payments"];
+ const args = dialog.get_values()["allocation"];
args.forEach(d => {
- frappe.model.set_value("Payment Reconciliation Payment", d.docname,
+ frappe.model.set_value("Payment Reconciliation Allocation", d.docname,
"difference_account", d.difference_account);
});
@@ -192,9 +217,9 @@
primary_action_label: __('Reconcile Entries')
});
- this.frm.doc.payments.forEach(d => {
+ this.frm.doc.allocation.forEach(d => {
if (d.difference_amount && !d.difference_account) {
- dialog.fields_dict.payments.df.data.push({
+ dialog.fields_dict.allocation.df.data.push({
'docname': d.name,
'reference_name': d.reference_name,
'difference_amount': d.difference_amount,
@@ -203,8 +228,8 @@
}
});
- this.data = dialog.fields_dict.payments.df.data;
- dialog.fields_dict.payments.grid.refresh();
+ this.data = dialog.fields_dict.allocation.df.data;
+ dialog.fields_dict.allocation.grid.refresh();
dialog.show();
} else {
this.reconcile_payment_entries();
@@ -218,48 +243,12 @@
doc: me.frm.doc,
method: 'reconcile',
callback: function(r, rt) {
- me.set_invoice_options();
- me.toggle_primary_action();
+ me.frm.clear_table("allocation");
+ me.frm.refresh_fields();
+ me.frm.refresh();
}
});
}
-
- set_invoice_options() {
- var me = this;
- var invoices = [];
-
- $.each(me.frm.doc.invoices || [], function(i, row) {
- if (row.invoice_number && !in_list(invoices, row.invoice_number))
- invoices.push(row.invoice_type + " | " + row.invoice_number);
- });
-
- if (invoices) {
- this.frm.fields_dict.payments.grid.update_docfield_property(
- 'invoice_number', 'options', "\n" + invoices.join("\n")
- );
-
- $.each(me.frm.doc.payments || [], function(i, p) {
- if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
- });
- }
-
- refresh_field("payments");
- }
-
- toggle_primary_action() {
- if ((this.frm.doc.payments || []).length) {
- this.frm.fields_dict.reconcile.$input
- && this.frm.fields_dict.reconcile.$input.addClass("btn-primary");
- this.frm.fields_dict.get_unreconciled_entries.$input
- && this.frm.fields_dict.get_unreconciled_entries.$input.removeClass("btn-primary");
- } else {
- this.frm.fields_dict.reconcile.$input
- && this.frm.fields_dict.reconcile.$input.removeClass("btn-primary");
- this.frm.fields_dict.get_unreconciled_entries.$input
- && this.frm.fields_dict.get_unreconciled_entries.$input.addClass("btn-primary");
- }
- }
-
};
extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
index cfb24c3..eb0c20f 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
@@ -1,622 +1,213 @@
{
- "allow_copy": 1,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2014-07-09 12:04:51.681583",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "actions": [],
+ "allow_copy": 1,
+ "creation": "2014-07-09 12:04:51.681583",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "party_type",
+ "column_break_4",
+ "party",
+ "receivable_payable_account",
+ "col_break1",
+ "from_invoice_date",
+ "from_payment_date",
+ "minimum_invoice_amount",
+ "minimum_payment_amount",
+ "column_break_11",
+ "to_invoice_date",
+ "to_payment_date",
+ "maximum_invoice_amount",
+ "maximum_payment_amount",
+ "column_break_13",
+ "invoice_limit",
+ "payment_limit",
+ "bank_cash_account",
+ "sec_break1",
+ "invoices",
+ "column_break_15",
+ "payments",
+ "sec_break2",
+ "allocation"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "party_type",
- "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": "Party Type",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "party_type",
+ "fieldtype": "Link",
+ "label": "Party Type",
+ "options": "DocType",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "party",
- "fieldtype": "Dynamic 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": "Party",
- "length": 0,
- "no_copy": 0,
- "options": "party_type",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:doc.party_type",
+ "fieldname": "party",
+ "fieldtype": "Dynamic Link",
+ "label": "Party",
+ "options": "party_type",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "receivable_payable_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Receivable / Payable Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "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
- },
+ "depends_on": "eval:doc.company && doc.party",
+ "fieldname": "receivable_payable_account",
+ "fieldtype": "Link",
+ "label": "Receivable / Payable Account",
+ "options": "Account",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bank_cash_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Bank / Cash Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "description": "This filter will be applied to Journal Entry.",
+ "fieldname": "bank_cash_account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Bank / Cash Account",
+ "options": "Account"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "col_break1",
- "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,
- "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
- },
+ "collapsible": 1,
+ "collapsible_depends_on": "eval: doc.invoices.length == 0",
+ "depends_on": "eval:doc.receivable_payable_account",
+ "fieldname": "col_break1",
+ "fieldtype": "Section Break",
+ "label": "Filters"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "from_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "From Invoice Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:(doc.payments).length || (doc.invoices).length",
+ "description": "If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.",
+ "fieldname": "sec_break1",
+ "fieldtype": "Section Break",
+ "label": "Unreconciled Entries"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "to_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "To Invoice Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "payments",
+ "fieldtype": "Table",
+ "label": "Payments",
+ "options": "Payment Reconciliation Payment"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "minimum_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Minimum Invoice Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "allocation",
+ "fieldname": "sec_break2",
+ "fieldtype": "Section Break",
+ "label": "Allocated Entries"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "maximum_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Maximum Invoice Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "invoices",
+ "fieldtype": "Table",
+ "label": "Invoices",
+ "options": "Payment Reconciliation Invoice"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "System will fetch all the entries if limit value is zero.",
- "fieldname": "limit",
- "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": "Limit",
- "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_15",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "get_unreconciled_entries",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Get Unreconciled Entries",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "allocation",
+ "fieldtype": "Table",
+ "label": "Allocation",
+ "options": "Payment Reconciliation Allocation"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sec_break1",
- "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": "Unreconciled Payment Details",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "payments",
- "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": "Payments",
- "length": 0,
- "no_copy": 0,
- "options": "Payment Reconciliation Payment",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "from_invoice_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "From Invoice Date"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reconcile",
- "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": "Reconcile",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "to_invoice_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "To Invoice Date"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sec_break2",
- "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": "Invoice/Journal Entry Details",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "minimum_invoice_amount",
+ "fieldtype": "Currency",
+ "label": "Minimum Invoice Amount"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "invoices",
- "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": "Invoices",
- "length": 0,
- "no_copy": 0,
- "options": "Payment Reconciliation Invoice",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "description": "System will fetch all the entries if limit value is zero.",
+ "fieldname": "invoice_limit",
+ "fieldtype": "Int",
+ "label": "Invoice Limit"
+ },
+ {
+ "fieldname": "column_break_13",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "from_payment_date",
+ "fieldtype": "Date",
+ "label": "From Payment Date"
+ },
+ {
+ "fieldname": "to_payment_date",
+ "fieldtype": "Date",
+ "label": "To Payment Date"
+ },
+ {
+ "fieldname": "minimum_payment_amount",
+ "fieldtype": "Currency",
+ "label": "Minimum Payment Amount"
+ },
+ {
+ "fieldname": "maximum_payment_amount",
+ "fieldtype": "Currency",
+ "label": "Maximum Payment Amount"
+ },
+ {
+ "description": "System will fetch all the entries if limit value is zero.",
+ "fieldname": "payment_limit",
+ "fieldtype": "Int",
+ "label": "Payment Limit"
+ },
+ {
+ "fieldname": "maximum_invoice_amount",
+ "fieldtype": "Currency",
+ "label": "Maximum Invoice Amount"
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 1,
- "icon": "icon-resize-horizontal",
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2019-01-15 17:42:21.135214",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Payment Reconciliation",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "hide_toolbar": 1,
+ "icon": "icon-resize-horizontal",
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-10-04 20:27:11.114194",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Reconciliation",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "read": 1,
+ "role": "Accounts Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "read": 1,
+ "role": "Accounts User",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "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 d788d91..9de79ae 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -2,14 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import flt, today
-from frappe import msgprint, _
+
+import frappe
+from frappe import _, msgprint
from frappe.model.document import Document
-from erpnext.accounts.utils import (get_outstanding_invoices,
- update_reference_in_payment_entry, reconcile_against_document)
+from frappe.utils import flt, getdate, nowdate, today
+
+import erpnext
+from erpnext.accounts.utils import get_outstanding_invoices, reconcile_against_document
from erpnext.controllers.accounts_controller import get_advance_payment_entries
+
class PaymentReconciliation(Document):
@frappe.whitelist()
def get_unreconciled_entries(self):
@@ -27,24 +30,32 @@
else:
dr_or_cr_notes = []
- self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
+ 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 = 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"
+ 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.limit)
+ 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")
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
if self.bank_cash_account else "1=1"
- limit_cond = "limit %s" % self.limit if self.limit else ""
-
journal_entries = frappe.db.sql("""
select
"Journal Entry" as reference_type, t1.name as reference_name,
@@ -56,7 +67,7 @@
where
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
and t2.party_type = %(party_type)s and t2.party = %(party)s
- and t2.account = %(account)s and {dr_or_cr} > 0
+ and t2.account = %(account)s and {dr_or_cr} > 0 {condition}
and (t2.reference_type is null or t2.reference_type = '' or
(t2.reference_type in ('Sales Order', 'Purchase Order')
and t2.reference_name is not null and t2.reference_name != ''))
@@ -65,11 +76,11 @@
THEN 1=1
ELSE {bank_account_condition}
END)
- order by t1.posting_date {limit_cond}
+ order by t1.posting_date
""".format(**{
"dr_or_cr": dr_or_cr,
"bank_account_condition": bank_account_condition,
- "limit_cond": limit_cond
+ "condition": condition
}), {
"party_type": self.party_type,
"party": self.party,
@@ -80,6 +91,7 @@
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")
@@ -90,7 +102,7 @@
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,
+ (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
@@ -100,15 +112,17 @@
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
+ 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)),
+ party_type_field=frappe.scrub(self.party_type),
+ condition=condition or ""),
{
'party': self.party,
'party_type': self.party_type,
@@ -116,22 +130,23 @@
'account': self.receivable_payable_account
}, as_dict=1)
- def add_payment_entries(self, entries):
+ def add_payment_entries(self, non_reconciled_payments):
self.set('payments', [])
- for e in entries:
+
+ for payment in non_reconciled_payments:
row = self.append('payments', {})
- row.update(e)
+ row.update(payment)
def get_invoice_entries(self):
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
- condition = self.check_condition()
+ condition = self.get_conditions(get_invoices=True)
non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party,
self.receivable_payable_account, condition=condition)
- if self.limit:
- non_reconciled_invoices = non_reconciled_invoices[:self.limit]
+ if self.invoice_limit:
+ non_reconciled_invoices = non_reconciled_invoices[:self.invoice_limit]
self.add_invoice_entries(non_reconciled_invoices)
@@ -139,41 +154,78 @@
#Populate 'invoices' with JVs and Invoices to reconcile against
self.set('invoices', [])
- for e in non_reconciled_invoices:
- ent = self.append('invoices', {})
- ent.invoice_type = e.get('voucher_type')
- ent.invoice_number = e.get('voucher_no')
- ent.invoice_date = e.get('posting_date')
- ent.amount = flt(e.get('invoice_amount'))
- ent.currency = e.get('currency')
- ent.outstanding_amount = e.get('outstanding_amount')
+ 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'))
@frappe.whitelist()
- def reconcile(self, args):
- for e in self.get('payments'):
- e.invoice_type = None
- if e.invoice_number and " | " in e.invoice_number:
- e.invoice_type, e.invoice_number = e.invoice_number.split(" | ")
+ 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
+ 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:
+ entries.append(res)
+ break
+ elif inv.get('outstanding_amount') == 0:
+ entries.append(res)
+ continue
+ else:
+ break
- self.get_invoice_entries()
- self.validate_invoice()
+ self.set('allocation', [])
+ for entry in entries:
+ 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')
+ })
+
+ @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")
- lst = []
+ entry_list = []
dr_or_cr_notes = []
- for e in self.get('payments'):
+ for row in self.get('allocation'):
reconciled_entry = []
- if e.invoice_number and e.allocated_amount:
- if e.reference_type in ['Sales Invoice', 'Purchase Invoice']:
+ if row.invoice_number and row.allocated_amount:
+ if row.reference_type in ['Sales Invoice', 'Purchase Invoice']:
reconciled_entry = dr_or_cr_notes
else:
- reconciled_entry = lst
+ reconciled_entry = entry_list
- reconciled_entry.append(self.get_payment_details(e, dr_or_cr))
+ reconciled_entry.append(self.get_payment_details(row, dr_or_cr))
- if lst:
- reconcile_against_document(lst)
+ if entry_list:
+ reconcile_against_document(entry_list)
if dr_or_cr_notes:
reconcile_dr_cr_note(dr_or_cr_notes, self.company)
@@ -183,98 +235,104 @@
def get_payment_details(self, row, dr_or_cr):
return frappe._dict({
- 'voucher_type': row.reference_type,
- 'voucher_no' : row.reference_name,
- 'voucher_detail_no' : row.reference_row,
- 'against_voucher_type' : row.invoice_type,
- 'against_voucher' : row.invoice_number,
+ '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.is_advance,
+ 'is_advance' : row.get('is_advance'),
'dr_or_cr' : dr_or_cr,
- 'unadjusted_amount' : flt(row.amount),
- 'allocated_amount' : flt(row.allocated_amount),
- 'difference_amount': row.difference_amount,
- 'difference_account': row.difference_account
+ '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')
})
- @frappe.whitelist()
- def get_difference_amount(self, child_row):
- if child_row.get("reference_type") != 'Payment Entry': return
-
- child_row = frappe._dict(child_row)
-
- if child_row.invoice_number and " | " in child_row.invoice_number:
- child_row.invoice_type, child_row.invoice_number = child_row.invoice_number.split(" | ")
-
- dr_or_cr = ("credit_in_account_currency"
- if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
-
- row = self.get_payment_details(child_row, dr_or_cr)
-
- doc = frappe.get_doc(row.voucher_type, row.voucher_no)
- update_reference_in_payment_entry(row, doc, do_not_save=True)
-
- return doc.difference_amount
-
def check_mandatory_to_fetch(self):
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
if not self.get(fieldname):
frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname)))
- def validate_invoice(self):
+ def validate_entries(self):
if not self.get("invoices"):
- frappe.throw(_("No records found in the Invoice table"))
+ frappe.throw(_("No records found in the Invoices table"))
if not self.get("payments"):
- frappe.throw(_("No records found in the Payment table"))
+ frappe.throw(_("No records found in the Payments table"))
+ def validate_allocation(self):
unreconciled_invoices = frappe._dict()
- for d in self.get("invoices"):
- unreconciled_invoices.setdefault(d.invoice_type, {}).setdefault(d.invoice_number, d.outstanding_amount)
+
+ for inv in self.get("invoices"):
+ unreconciled_invoices.setdefault(inv.invoice_type, {}).setdefault(inv.invoice_number, inv.outstanding_amount)
invoices_to_reconcile = []
- for p in self.get("payments"):
- if p.invoice_type and p.invoice_number and p.allocated_amount:
- invoices_to_reconcile.append(p.invoice_number)
+ for row in self.get("allocation"):
+ if row.invoice_type and row.invoice_number and row.allocated_amount:
+ invoices_to_reconcile.append(row.invoice_number)
- if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
- frappe.throw(_("{0}: {1} not found in Invoice Details table")
- .format(p.invoice_type, p.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))
- if flt(p.allocated_amount) > flt(p.amount):
- frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2}")
- .format(p.idx, p.allocated_amount, p.amount))
-
- invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)
- if flt(p.allocated_amount) - invoice_outstanding > 0.009:
- frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}")
- .format(p.idx, p.allocated_amount, invoice_outstanding))
+ 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))
if not invoices_to_reconcile:
- frappe.throw(_("Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row"))
+ frappe.throw(_("No records found in Allocation table"))
- def check_condition(self):
- cond = " and posting_date >= {0}".format(frappe.db.escape(self.from_date)) if self.from_date else ""
- cond += " and posting_date <= {0}".format(frappe.db.escape(self.to_date)) if self.to_date else ""
- dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
- else "credit_in_account_currency")
+ def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False):
+ condition = " and company = '{0}' ".format(self.company)
- if self.minimum_amount:
- cond += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_amount))
- if self.maximum_amount:
- cond += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_amount))
+ 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")
- return cond
+ 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))
+
+ 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.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))
+
+ 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.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 d in dr_cr_notes:
+ for inv in dr_cr_notes:
voucher_type = ('Credit Note'
- if d.voucher_type == 'Sales Invoice' else 'Debit Note')
+ if inv.voucher_type == 'Sales Invoice' else 'Debit Note')
reconcile_dr_or_cr = ('debit_in_account_currency'
- if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
+ if inv.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
company_currency = erpnext.get_company_currency(company)
@@ -283,28 +341,28 @@
"voucher_type": voucher_type,
"posting_date": today(),
"company": company,
- "multi_currency": 1 if d.currency != company_currency else 0,
+ "multi_currency": 1 if inv.currency != company_currency else 0,
"accounts": [
{
- 'account': d.account,
- 'party': d.party,
- 'party_type': d.party_type,
- d.dr_or_cr: abs(d.allocated_amount),
- 'reference_type': d.against_voucher_type,
- 'reference_name': d.against_voucher,
+ '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': d.account,
- 'party': d.party,
- 'party_type': d.party_type,
- reconcile_dr_or_cr: (abs(d.allocated_amount)
- if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
- 'reference_type': d.voucher_type,
- 'reference_name': d.voucher_no,
+ '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()
\ No newline at end of file
+ jv.submit()
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
new file mode 100644
index 0000000..2271f48
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestPaymentReconciliation(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/payment_reconciliation_allocation/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/payment_reconciliation_allocation/__init__.py
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
new file mode 100644
index 0000000..6a21692
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
@@ -0,0 +1,145 @@
+{
+ "actions": [],
+ "creation": "2021-08-16 17:04:40.185167",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "reference_type",
+ "reference_name",
+ "reference_row",
+ "column_break_3",
+ "invoice_type",
+ "invoice_number",
+ "section_break_6",
+ "allocated_amount",
+ "unreconciled_amount",
+ "column_break_8",
+ "amount",
+ "is_advance",
+ "section_break_5",
+ "difference_amount",
+ "column_break_7",
+ "difference_account"
+ ],
+ "fields": [
+ {
+ "fieldname": "invoice_number",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Invoice Number",
+ "options": "invoice_type",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Allocated Amount",
+ "options": "Currency",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "difference_account",
+ "fieldtype": "Link",
+ "label": "Difference Account",
+ "options": "Account",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_7",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "difference_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Difference Amount",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Reference Name",
+ "options": "reference_type",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "is_advance",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Is Advance",
+ "read_only": 1
+ },
+ {
+ "fieldname": "reference_type",
+ "fieldtype": "Link",
+ "label": "Reference Type",
+ "options": "DocType",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "invoice_type",
+ "fieldtype": "Link",
+ "label": "Invoice Type",
+ "options": "DocType",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "unreconciled_amount",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Unreconciled Amount",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Amount",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "reference_row",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Reference Row",
+ "read_only": 1
+ }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2021-10-06 11:48:59.616562",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Reconciliation Allocation",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.py b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.py
new file mode 100644
index 0000000..9db8e62
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class PaymentReconciliationAllocation(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
index 6a79a85..00c9e12 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json
@@ -44,7 +44,6 @@
{
"fieldname": "amount",
"fieldtype": "Currency",
- "in_list_view": 1,
"label": "Amount",
"options": "currency",
"read_only": 1
@@ -67,7 +66,7 @@
],
"istable": 1,
"links": [],
- "modified": "2020-07-19 18:12:27.964073",
+ "modified": "2021-08-24 22:42:40.923179",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Invoice",
diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
index 800d800..5ac1855 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
+++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PaymentReconciliationInvoice(Document):
pass
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
index 925a6f1..add07e8 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
@@ -11,11 +11,7 @@
"is_advance",
"reference_row",
"col_break1",
- "invoice_number",
"amount",
- "allocated_amount",
- "section_break_10",
- "difference_account",
"difference_amount",
"sec_break1",
"remark",
@@ -41,6 +37,7 @@
{
"fieldname": "posting_date",
"fieldtype": "Date",
+ "in_list_view": 1,
"label": "Posting Date",
"read_only": 1
},
@@ -64,14 +61,6 @@
},
{
"columns": 2,
- "fieldname": "invoice_number",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Invoice Number",
- "reqd": 1
- },
- {
- "columns": 2,
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
@@ -80,56 +69,33 @@
"read_only": 1
},
{
- "columns": 2,
- "fieldname": "allocated_amount",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Allocated amount",
- "options": "currency",
- "reqd": 1
- },
- {
"fieldname": "sec_break1",
"fieldtype": "Section Break"
},
{
"fieldname": "remark",
"fieldtype": "Small Text",
- "in_list_view": 1,
"label": "Remark",
"read_only": 1
},
{
- "columns": 2,
- "fieldname": "difference_account",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Difference Account",
- "options": "Account"
- },
- {
- "fieldname": "difference_amount",
- "fieldtype": "Currency",
- "label": "Difference Amount",
- "options": "currency",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "section_break_10",
- "fieldtype": "Section Break"
- },
- {
"fieldname": "currency",
"fieldtype": "Link",
"hidden": 1,
"label": "Currency",
"options": "Currency"
+ },
+ {
+ "fieldname": "difference_amount",
+ "fieldtype": "Currency",
+ "label": "Difference Amount",
+ "options": "currency",
+ "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2020-07-19 18:12:41.682347",
+ "modified": "2021-08-30 10:51:48.140062",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
index c76f785..78c84ff 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PaymentReconciliationPayment(Document):
pass
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 438951d..2c96749 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -3,18 +3,25 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import json
+
import frappe
from frappe import _
+from frappe.integrations.utils import get_payment_gateway_controller
from frappe.model.document import Document
-from frappe.utils import flt, nowdate, get_url
+from frappe.utils import flt, get_url, nowdate
+from frappe.utils.background_jobs import enqueue
+
+from erpnext.accounts.doctype.payment_entry.payment_entry import (
+ get_company_defaults,
+ get_payment_entry,
+)
+from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
from erpnext.accounts.party import get_party_account, get_party_bank_account
from erpnext.accounts.utils import get_account_currency
-from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults
-from frappe.integrations.utils import get_payment_gateway_controller
-from frappe.utils.background_jobs import enqueue
from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription
-from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
+
class PaymentRequest(Document):
def validate(self):
@@ -541,4 +548,12 @@
}
}, target_doc, set_missing_values)
- return doclist
\ No newline at end of file
+ return doclist
+
+def validate_payment(doc, method=""):
+ if not frappe.db.has_column(doc.reference_doctype, 'status'):
+ return
+
+ status = frappe.db.get_value(doc.reference_doctype, doc.reference_docname, 'status')
+ if status == 'Paid':
+ frappe.throw(_("The Payment Request {0} is already paid, cannot process payment twice").format(doc.reference_docname))
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.js b/erpnext/accounts/doctype/payment_request/test_payment_request.js
deleted file mode 100644
index 070b595..0000000
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Request", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Payment Request
- () => frappe.tests.make('Payment Request', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index 5eba62c..c97c873 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -3,11 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
+import frappe
+
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.setup.utils import get_exchange_rate
test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"]
@@ -138,4 +140,4 @@
# Try to make Payment Request more than SO amount, should give validation
pr2.grand_total = 900
- self.assertRaises(frappe.ValidationError, pr2.save)
\ No newline at end of file
+ self.assertRaises(frappe.ValidationError, pr2.save)
diff --git a/erpnext/accounts/doctype/payment_schedule/payment_schedule.py b/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
index 4174017..33d5efa 100644
--- a/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
+++ b/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.js b/erpnext/accounts/doctype/payment_term/payment_term.js
index acd0144..feecf93 100644
--- a/erpnext/accounts/doctype/payment_term/payment_term.js
+++ b/erpnext/accounts/doctype/payment_term/payment_term.js
@@ -19,4 +19,4 @@
frm.set_df_property("discount", "description", description);
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.py b/erpnext/accounts/doctype/payment_term/payment_term.py
index 5d4df05..a04c183 100644
--- a/erpnext/accounts/doctype/payment_term/payment_term.py
+++ b/erpnext/accounts/doctype/payment_term/payment_term.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
diff --git a/erpnext/accounts/doctype/payment_term/test_payment_term.js b/erpnext/accounts/doctype/payment_term/test_payment_term.js
deleted file mode 100644
index b26e42a..0000000
--- a/erpnext/accounts/doctype/payment_term/test_payment_term.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Term", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Payment Term
- () => frappe.tests.make('Payment Term', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/payment_term/test_payment_term.py b/erpnext/accounts/doctype/payment_term/test_payment_term.py
index d9baa59..bc0645f 100644
--- a/erpnext/accounts/doctype/payment_term/test_payment_term.py
+++ b/erpnext/accounts/doctype/payment_term/test_payment_term.py
@@ -2,6 +2,7 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
import unittest
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
index 84c8d09..ea18ade 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
@@ -3,6 +3,6 @@
frappe.ui.form.on('Payment Terms Template', {
setup: function(frm) {
-
+
}
});
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 39627eb..3568591 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
@@ -5,9 +5,9 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.document import Document
-from frappe.utils import flt, cint
from frappe import _
+from frappe.model.document import Document
+from frappe.utils import flt
class PaymentTermsTemplate(Document):
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 c705097..5c8cb4f 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
@@ -30,4 +30,4 @@
'items': ['Customer Group', 'Supplier Group']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js
deleted file mode 100644
index 494a0ed..0000000
--- a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payment Terms Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Payment Terms Template
- () => frappe.tests.make('Payment Terms Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 6daaf1e..2052a50 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
@@ -2,6 +2,7 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
import unittest
import frappe
diff --git a/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py b/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py
index 54c0fda..c857a88 100644
--- a/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py
+++ b/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
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 9cfb478..888bf9f 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -2,13 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt
from frappe import _
+from frappe.utils import flt
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimensions,
+)
from erpnext.accounts.utils import get_account_currency
from erpnext.controllers.accounts_controller import AccountsController
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions,
- get_dimensions)
+
class PeriodClosingVoucher(AccountsController):
def validate(self):
@@ -50,9 +55,13 @@
.format(pce[0][0], self.posting_date))
def make_gl_entries(self):
- gl_entries = []
- net_pl_balance = 0
+ 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):
+ gl_entries = []
pl_accounts = self.get_pl_balances()
for acc in pl_accounts:
@@ -60,6 +69,7 @@
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,
@@ -67,35 +77,13 @@
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
}, item=acc))
- net_pl_balance += flt(acc.bal_in_company_currency)
+ if gl_entries:
+ gle_for_net_pl_bal = self.get_pnl_gl_entry(pl_accounts)
+ gl_entries += gle_for_net_pl_bal
- if net_pl_balance:
- if self.cost_center_wise_pnl:
- costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts)
- gl_entries += costcenter_wise_gl_entries
- else:
- gl_entry = self.get_pnl_gl_entry(net_pl_balance)
- gl_entries.append(gl_entry)
+ return gl_entries
- from erpnext.accounts.general_ledger import make_gl_entries
- make_gl_entries(gl_entries)
-
- def get_pnl_gl_entry(self, net_pl_balance):
- cost_center = frappe.db.get_value("Company", self.company, "cost_center")
- gl_entry = self.get_gl_dict({
- "account": self.closing_account_head,
- "debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
- "debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
- "credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
- "credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
- "cost_center": cost_center
- })
-
- self.update_default_dimensions(gl_entry)
-
- return gl_entry
-
- def get_costcenter_wise_pnl_gl_entries(self, pl_accounts):
+ def get_pnl_gl_entry(self, pl_accounts):
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
gl_entries = []
@@ -104,6 +92,7 @@
gl_entry = self.get_gl_dict({
"account": self.closing_account_head,
"cost_center": acc.cost_center or company_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,
@@ -130,7 +119,7 @@
def get_pl_balances(self):
"""Get balance for dimension-wise pl accounts"""
- dimension_fields = ['t1.cost_center']
+ dimension_fields = ['t1.cost_center', 't1.finance_book']
self.accounting_dimensions = get_accounting_dimensions()
for dimension in self.accounting_dimensions:
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 2f29372..2d417a4 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
@@ -3,68 +3,63 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from frappe.utils import flt, today
-from erpnext.accounts.utils import get_fiscal_year, now
+from frappe.utils import today
+
+from erpnext.accounts.doctype.finance_book.test_finance_book import create_finance_book
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
+from erpnext.accounts.utils import get_fiscal_year, now
+
class TestPeriodClosingVoucher(unittest.TestCase):
def test_closing_entry(self):
- year_start_date = get_fiscal_year(today(), company="_Test Company")[1]
+ frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
- make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
- "_Test Cost Center - _TC", posting_date=now(), submit=True)
+ company = create_company()
+ cost_center = create_cost_center('Test Cost Center 1')
- make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 600, "_Test Cost Center - _TC", posting_date=now(), submit=True)
+ jv1 = make_journal_entry(
+ amount=400,
+ account1="Cash - TPC",
+ account2="Sales - TPC",
+ cost_center=cost_center,
+ posting_date=now(),
+ save=False
+ )
+ jv1.company = company
+ jv1.save()
+ jv1.submit()
- random_expense_account = frappe.db.sql("""
- select t1.account,
- sum(t1.debit) - sum(t1.credit) as balance,
- sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) \
- as balance_in_account_currency
- from `tabGL Entry` t1, `tabAccount` t2
- where t1.account = t2.name and t2.root_type = 'Expense'
- and t2.docstatus < 2 and t2.company = '_Test Company'
- and t1.posting_date between %s and %s
- group by t1.account
- having sum(t1.debit) > sum(t1.credit)
- limit 1""", (year_start_date, today()), as_dict=True)
-
- profit_or_loss = frappe.db.sql("""select sum(t1.debit) - sum(t1.credit) as balance
- from `tabGL Entry` t1, `tabAccount` t2
- where t1.account = t2.name and t2.report_type = 'Profit and Loss'
- and t2.docstatus < 2 and t2.company = '_Test Company'
- and t1.posting_date between %s and %s""", (year_start_date, today()))
-
- profit_or_loss = flt(profit_or_loss[0][0]) if profit_or_loss else 0
+ jv2 = make_journal_entry(
+ amount=600,
+ account1="Cost of Goods Sold - TPC",
+ account2="Cash - TPC",
+ cost_center=cost_center,
+ posting_date=now(),
+ save=False
+ )
+ jv2.company = company
+ jv2.save()
+ jv2.submit()
pcv = self.make_period_closing_voucher()
+ surplus_account = pcv.closing_account_head
- # Check value for closing account
- gle_amount_for_closing_account = frappe.db.sql("""select debit - credit
- from `tabGL Entry` where voucher_type='Period Closing Voucher' and voucher_no=%s
- and account = '_Test Account Reserves and Surplus - _TC'""", pcv.name)
+ expected_gle = (
+ ('Cost of Goods Sold - TPC', 0.0, 600.0),
+ (surplus_account, 600.0, 400.0),
+ ('Sales - TPC', 400.0, 0.0)
+ )
- gle_amount_for_closing_account = flt(gle_amount_for_closing_account[0][0]) \
- if gle_amount_for_closing_account else 0
+ pcv_gle = frappe.db.sql("""
+ select account, debit, credit from `tabGL Entry` where voucher_no=%s order by account
+ """, (pcv.name))
- self.assertEqual(gle_amount_for_closing_account, profit_or_loss)
-
- if random_expense_account:
- # Check posted value for teh above random_expense_account
- gle_for_random_expense_account = frappe.db.sql("""
- select sum(debit - credit) as amount,
- sum(debit_in_account_currency - credit_in_account_currency) as amount_in_account_currency
- from `tabGL Entry`
- where voucher_type='Period Closing Voucher' and voucher_no=%s and account =%s""",
- (pcv.name, random_expense_account[0].account), as_dict=True)
-
- self.assertEqual(gle_for_random_expense_account[0].amount, -1*random_expense_account[0].balance)
- self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
- -1*random_expense_account[0].balance_in_account_currency)
+ self.assertEqual(pcv_gle, expected_gle)
def test_cost_center_wise_posting(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
@@ -92,40 +87,80 @@
debit_to="Debtors - TPC"
)
- pcv = frappe.get_doc({
- "transaction_date": today(),
- "posting_date": today(),
- "fiscal_year": get_fiscal_year(today())[0],
- "company": "Test PCV Company",
- "cost_center_wise_pnl": 1,
- "closing_account_head": surplus_account,
- "remarks": "Test",
- "doctype": "Period Closing Voucher"
- })
- pcv.insert()
- pcv.submit()
+ pcv = self.make_period_closing_voucher()
+ surplus_account = pcv.closing_account_head
expected_gle = (
- ('Sales - TPC', 200.0, 0.0, cost_center2),
+ (surplus_account, 0.0, 400.0, cost_center1),
(surplus_account, 0.0, 200.0, cost_center2),
('Sales - TPC', 400.0, 0.0, cost_center1),
- (surplus_account, 0.0, 400.0, cost_center1)
+ ('Sales - TPC', 200.0, 0.0, cost_center2),
)
pcv_gle = frappe.db.sql("""
- select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s
+ select account, debit, credit, cost_center
+ from `tabGL Entry` where voucher_no=%s
+ order by account, cost_center
""", (pcv.name))
- self.assertTrue(pcv_gle, expected_gle)
+ self.assertEqual(pcv_gle, expected_gle)
+
+ def test_period_closing_with_finance_book_entries(self):
+ frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
+
+ company = create_company()
+ surplus_account = create_account()
+ cost_center = create_cost_center("Test Cost Center 1")
+
+ 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"
+ )
+ jv = make_journal_entry(
+ account1="Cash - TPC",
+ account2="Sales - TPC",
+ amount=400,
+ cost_center=cost_center,
+ posting_date=now()
+ )
+ jv.company = company
+ jv.finance_book = create_finance_book().name
+ jv.save()
+ jv.submit()
+
+ pcv = self.make_period_closing_voucher()
+ surplus_account = pcv.closing_account_head
+
+ 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)
+ )
+
+ 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))
+
+ self.assertEqual(pcv_gle, expected_gle)
def make_period_closing_voucher(self):
+ surplus_account = create_account()
+ cost_center = create_cost_center("Test Cost Center 1")
pcv = frappe.get_doc({
"doctype": "Period Closing Voucher",
- "closing_account_head": "_Test Account Reserves and Surplus - _TC",
- "company": "_Test Company",
- "fiscal_year": get_fiscal_year(today(), company="_Test Company")[0],
+ "transaction_date": today(),
"posting_date": today(),
- "cost_center": "_Test Cost Center - _TC",
+ "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()
@@ -139,7 +174,7 @@
'company_name': "Test PCV Company",
'country': 'United States',
'default_currency': 'USD'
- })
+ })
company.insert(ignore_if_duplicate = True)
return company.name
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 6418d73..264d4a6 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -20,9 +20,9 @@
frm.set_query("pos_opening_entry", function(doc) {
return { filters: { 'status': 'Open', 'docstatus': 1 } };
});
-
+
if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime());
-
+
frappe.realtime.on('closing_process_complete', async function(data) {
await frm.reload_doc();
if (frm.doc.status == 'Failed' && frm.doc.error_message && data.user == frappe.session.user) {
@@ -43,7 +43,7 @@
const issue = '<a id="jump_to_error" style="text-decoration: underline;">issue</a>';
frm.dashboard.set_headline(
__('POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.', [issue]));
-
+
$('#jump_to_error').on('click', (e) => {
e.preventDefault();
frappe.utils.scroll_to(
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 8252872..896ebdf 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -3,13 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import json
from frappe import _
-from frappe.utils import get_datetime, flt
+from frappe.utils import flt, get_datetime
+
+from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
+ consolidate_pos_invoices,
+ unconsolidate_pos_invoices,
+)
from erpnext.controllers.status_updater import StatusUpdater
-from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
-from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices, unconsolidate_pos_invoices
+
class POSClosingEntry(StatusUpdater):
def validate(self):
diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.js
deleted file mode 100644
index 48109b1..0000000
--- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Closing Entry", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new POS Closing Entry
- () => frappe.tests.make('POS Closing Entry', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 b596c0c..44c87d6 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
@@ -2,14 +2,19 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
-from frappe.utils import nowdate
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+import frappe
+
+from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import (
+ make_closing_entry_from_opening,
+)
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
-from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import make_closing_entry_from_opening
from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
class TestPOSClosingEntry(unittest.TestCase):
def setUp(self):
@@ -85,9 +90,15 @@
pcv_doc.load_from_db()
pcv_doc.cancel()
- si_doc.load_from_db()
+
+ cancelled_invoice = frappe.db.get_value(
+ 'POS Invoice Merge Log', {'pos_closing_entry': pcv_doc.name},
+ 'consolidated_invoice'
+ )
+ docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, 'docstatus')
+ self.assertEqual(docstatus, 2)
+
pos_inv1.load_from_db()
- self.assertEqual(si_doc.docstatus, 2)
self.assertEqual(pos_inv1.status, 'Paid')
diff --git a/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.py b/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.py
index 46b6c77..4293abd 100644
--- a/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.py
+++ b/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class POSClosingEntryDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_closing_entry_taxes/pos_closing_entry_taxes.py b/erpnext/accounts/doctype/pos_closing_entry_taxes/pos_closing_entry_taxes.py
index f72d9a6..74cf754 100644
--- a/erpnext/accounts/doctype/pos_closing_entry_taxes/pos_closing_entry_taxes.py
+++ b/erpnext/accounts/doctype/pos_closing_entry_taxes/pos_closing_entry_taxes.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class POSClosingEntryTaxes(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_customer_group/pos_customer_group.py b/erpnext/accounts/doctype/pos_customer_group/pos_customer_group.py
index 85c1c9f..570eb9e 100644
--- a/erpnext/accounts/doctype/pos_customer_group/pos_customer_group.py
+++ b/erpnext/accounts/doctype/pos_customer_group/pos_customer_group.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class POSCustomerGroup(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_field/pos_field.py b/erpnext/accounts/doctype/pos_field/pos_field.py
index b4720b3..00faebb 100644
--- a/erpnext/accounts/doctype/pos_field/pos_field.py
+++ b/erpnext/accounts/doctype/pos_field/pos_field.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSField(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index 181e9f8..15c2922 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -16,7 +16,7 @@
onload(doc) {
super.onload();
- this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
+ this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry'];
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
this.frm.script_manager.trigger("is_pos");
this.frm.refresh_fields();
@@ -111,16 +111,12 @@
}
write_off_outstanding_amount_automatically() {
- if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
+ if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
// this will make outstanding amount 0
this.frm.set_value("write_off_amount",
flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount"))
);
- this.frm.toggle_enable("write_off_amount", false);
-
- } else {
- this.frm.toggle_enable("write_off_amount", true);
}
this.calculate_outstanding_amount(false);
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 33c3e04..bff8587 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -99,6 +99,7 @@
"loyalty_redemption_account",
"loyalty_redemption_cost_center",
"section_break_49",
+ "coupon_code",
"apply_discount_on",
"base_discount_amount",
"column_break_51",
@@ -595,7 +596,8 @@
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -620,6 +622,7 @@
"read_only": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"label": "Packing List",
@@ -627,6 +630,7 @@
"print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"label": "Packed Items",
@@ -1182,7 +1186,8 @@
"label": "Write Off Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1
+ "print_hide": 1,
+ "read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically"
},
{
"fieldname": "base_write_off_amount",
@@ -1548,12 +1553,20 @@
"no_copy": 1,
"options": "Sales Invoice",
"read_only": 1
+ },
+ {
+ "depends_on": "coupon_code",
+ "fieldname": "coupon_code",
+ "fieldtype": "Link",
+ "label": "Coupon Code",
+ "options": "Coupon Code",
+ "print_hide": 1
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2021-07-29 13:37:20.636171",
+ "modified": "2021-08-27 20:12:57.306772",
"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 8ec4ef2..27d678b 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -3,19 +3,24 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.model.document import Document
-from erpnext.accounts.utils import get_account_currency
-from erpnext.accounts.party import get_party_account, get_due_date
-from frappe.utils import cint, flt, getdate, nowdate, get_link_to_form
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
-from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
-from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
-from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice, get_bank_cash_account, update_multi_mode_option, get_mode_of_payment_info
-
+from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
from six import iteritems
+from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
+from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+ SalesInvoice,
+ get_bank_cash_account,
+ get_mode_of_payment_info,
+ update_multi_mode_option,
+)
+from erpnext.accounts.party import get_due_date, get_party_account
+from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
+
+
class POSInvoice(SalesInvoice):
def __init__(self, *args, **kwargs):
super(POSInvoice, self).__init__(*args, **kwargs)
@@ -35,6 +40,7 @@
self.validate_change_amount()
self.validate_change_account()
self.validate_item_cost_centers()
+ self.validate_warehouse()
self.validate_serialised_or_batched_item()
self.validate_stock_availablility()
self.validate_return_items_qty()
@@ -44,6 +50,9 @@
self.validate_pos()
self.validate_payment_amount()
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
@@ -58,6 +67,10 @@
self.check_phone_payments()
self.set_status(update=True)
+ if self.coupon_code:
+ from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
+ 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:
pos_closing_entry = frappe.get_all(
@@ -84,6 +97,10 @@
against_psi_doc.delete_loyalty_point_entry()
against_psi_doc.make_loyalty_point_entry()
+ if self.coupon_code:
+ from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
+ 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:
@@ -127,7 +144,7 @@
.format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable"))
def validate_stock_availablility(self):
- if self.is_return:
+ if self.is_return or self.docstatus != 1:
return
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
@@ -298,7 +315,7 @@
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_item_details, get_pos_profile
+ 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:
@@ -508,6 +525,7 @@
@frappe.whitelist()
def make_merge_log(invoices):
import json
+
from six import string_types
if isinstance(invoices, string_types):
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 6172796..e6e0dd2 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -3,14 +3,18 @@
# See license.txt
from __future__ import unicode_literals
+import copy
+import unittest
+
import frappe
-import unittest, copy, time
-from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
+
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
-from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
-from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+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 TestPOSInvoice(unittest.TestCase):
@classmethod
@@ -213,8 +217,8 @@
self.assertEqual(pos_return.get('payments')[1].amount, -500)
def test_pos_return_for_serialized_item(self):
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ 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')
@@ -239,8 +243,8 @@
self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0])
def test_partial_pos_returns(self):
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ 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')
@@ -293,8 +297,8 @@
self.assertRaises(frappe.ValidationError, inv.insert)
def test_serialized_item_transaction(self):
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ 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')
@@ -320,11 +324,12 @@
pos2.get("items")[0].serial_no = serial_nos[0]
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
- self.assertRaises(frappe.ValidationError, pos2.insert)
+ pos2.insert()
+ self.assertRaises(frappe.ValidationError, pos2.submit)
def test_delivered_serialized_item_transaction(self):
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ 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')
@@ -348,11 +353,14 @@
pos2.get("items")[0].serial_no = serial_nos[0]
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
- self.assertRaises(frappe.ValidationError, pos2.insert)
+ pos2.insert()
+ self.assertRaises(frappe.ValidationError, pos2.submit)
def test_loyalty_points(self):
+ from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
+ get_loyalty_program_details_with_points,
+ )
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
- from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
create_records()
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
@@ -372,7 +380,10 @@
self.assertEqual(after_cancel_lp_details.loyalty_points, before_lp_details.loyalty_points)
def test_loyalty_points_redeemption(self):
- from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
+ from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
+ get_loyalty_program_details_with_points,
+ )
+
# add 10 loyalty points
create_pos_invoice(customer="Test Loyalty Customer", rate=10000)
@@ -390,8 +401,12 @@
self.assertEqual(after_redeem_lp_details.loyalty_points, 9)
def test_merging_into_sales_invoice_with_discount(self):
- from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
- from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
+ init_user_and_profile,
+ )
+ from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
+ consolidate_pos_invoices,
+ )
frappe.db.sql("delete from `tabPOS Invoice`")
test_user, pos_profile = init_user_and_profile()
@@ -414,8 +429,12 @@
self.assertEqual(rounded_total, 3470)
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
- from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
- from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
+ init_user_and_profile,
+ )
+ from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
+ consolidate_pos_invoices,
+ )
frappe.db.sql("delete from `tabPOS Invoice`")
test_user, pos_profile = init_user_and_profile()
@@ -455,8 +474,12 @@
self.assertEqual(rounded_total, 840)
def test_merging_with_validate_selling_price(self):
- from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
- from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
+ init_user_and_profile,
+ )
+ from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
+ consolidate_pos_invoices,
+ )
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
index 92ce61b..99e4719 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSInvoiceItem(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js
index cd08efc..73c6290 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js
@@ -4,13 +4,18 @@
frappe.ui.form.on('POS Invoice Merge Log', {
setup: function(frm) {
frm.set_query("pos_invoice", "pos_invoices", doc => {
- return{
- filters: {
+ return {
+ filters: {
'docstatus': 1,
- 'customer': doc.customer,
- 'consolidated_invoice': ''
+ 'customer': doc.customer,
+ 'consolidated_invoice': ''
}
}
});
+ },
+
+ merge_invoices_based_on: function(frm) {
+ frm.set_value('customer', '');
+ frm.set_value('customer_group', '');
}
});
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
index da2984f..d762087 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
@@ -6,9 +6,11 @@
"engine": "InnoDB",
"field_order": [
"posting_date",
- "customer",
+ "merge_invoices_based_on",
"column_break_3",
"pos_closing_entry",
+ "customer",
+ "customer_group",
"section_break_3",
"pos_invoices",
"references_section",
@@ -88,12 +90,27 @@
"fieldtype": "Link",
"label": "POS Closing Entry",
"options": "POS Closing Entry"
+ },
+ {
+ "fieldname": "merge_invoices_based_on",
+ "fieldtype": "Select",
+ "label": "Merge Invoices Based On",
+ "options": "Customer\nCustomer Group",
+ "reqd": 1
+ },
+ {
+ "depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "label": "Customer Group",
+ "mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
+ "options": "Customer Group"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-12-01 11:53:57.267579",
+ "modified": "2021-09-14 11:17:19.001142",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Merge Log",
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 08e072e..9dae3a7 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
@@ -3,17 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import json
+
import frappe
+import six
from frappe import _
-from frappe.model import default_fields
+from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.model.document import Document
+from frappe.model.mapper import map_child_doc, map_doc
from frappe.utils import flt, getdate, nowdate
from frappe.utils.background_jobs import enqueue
-from frappe.model.mapper import map_doc, map_child_doc
from frappe.utils.scheduler import is_scheduler_inactive
-from frappe.core.page.background_jobs.background_jobs import get_info
-import json
-import six
+
class POSInvoiceMergeLog(Document):
def validate(self):
@@ -21,6 +23,9 @@
self.validate_pos_invoice_status()
def validate_customer(self):
+ 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))
@@ -122,7 +127,7 @@
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):
+ i.uom == item.uom and i.net_rate == item.net_rate and i.warehouse == item.warehouse):
found = True
i.qty = i.qty + item.qty
@@ -170,6 +175,11 @@
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':
+ invoice.flags.ignore_pos_profile = True
+ invoice.pos_profile = ''
return invoice
@@ -226,7 +236,7 @@
return pos_invoices
def get_invoice_customer_map(pos_invoices):
- # pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Custoemr 2' : [{}] }
+ # pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Customer 2' : [{}] }
pos_invoice_customer_map = {}
for invoice in pos_invoices:
customer = invoice.get('customer')
@@ -354,4 +364,4 @@
except Exception:
json_message = message
- return json_message
\ No newline at end of file
+ return json_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 040a815..c531bb9 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
@@ -3,13 +3,18 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
import json
-from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
-from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
-from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
+import unittest
+
+import frappe
+
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
+from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
+ consolidate_pos_invoices,
+)
+
class TestPOSInvoiceMergeLog(unittest.TestCase):
def test_consolidated_invoice_creation(self):
@@ -147,4 +152,3 @@
frappe.set_user("Administrator")
frappe.db.sql("delete from `tabPOS Profile`")
frappe.db.sql("delete from `tabPOS Invoice`")
-
diff --git a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.py b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.py
index 4c45265..9bce082 100644
--- a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.py
+++ b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSInvoiceReference(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_item_group/pos_item_group.py b/erpnext/accounts/doctype/pos_item_group/pos_item_group.py
index ceaa57b..b5ff794 100644
--- a/erpnext/accounts/doctype/pos_item_group/pos_item_group.py
+++ b/erpnext/accounts/doctype/pos_item_group/pos_item_group.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class POSItemGroup(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js
index 372e756..d23f348 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js
@@ -53,4 +53,4 @@
});
}
}
-});
\ 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 0023a84..979479f 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import cint, get_link_to_form
+
from erpnext.controllers.status_updater import StatusUpdater
+
class POSOpeningEntry(StatusUpdater):
def validate(self):
self.validate_pos_profile_and_cashier()
@@ -38,4 +41,4 @@
frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
def on_submit(self):
- self.set_status(update=True)
\ No newline at end of file
+ self.set_status(update=True)
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 2e36391..35a2b58 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
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestPOSOpeningEntry(unittest.TestCase):
pass
@@ -21,8 +23,8 @@
balance_details.append(frappe._dict({
'mode_of_payment': d.mode_of_payment
}))
-
+
entry.set("balance_details", balance_details)
entry.submit()
-
- return entry.as_dict()
+
+ return entry.as_dict()
diff --git a/erpnext/accounts/doctype/pos_opening_entry_detail/pos_opening_entry_detail.py b/erpnext/accounts/doctype/pos_opening_entry_detail/pos_opening_entry_detail.py
index 5557062..be5d876 100644
--- a/erpnext/accounts/doctype/pos_opening_entry_detail/pos_opening_entry_detail.py
+++ b/erpnext/accounts/doctype/pos_opening_entry_detail/pos_opening_entry_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSOpeningEntryDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.py b/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.py
index 8a46d84..851d8ef 100644
--- a/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.py
+++ b/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSPaymentMethod(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index cf7ed26..b64e2ed 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
-from frappe.utils import cint, now, get_link_to_form
-from six import iteritems
+from frappe import _, msgprint
from frappe.model.document import Document
+from frappe.utils import get_link_to_form, now
+from six import iteritems
+
class POSProfile(Document):
def validate(self):
diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.js b/erpnext/accounts/doctype/pos_profile/test_pos_profile.js
deleted file mode 100644
index 42e5b7f..0000000
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Profile", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('POS Profile', [
- // insert a new POS Profile
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
index 0033965..7c53f4a 100644
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
@@ -3,10 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.stock.get_item_details import get_pos_profile
+
+import frappe
+
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']
@@ -31,7 +33,9 @@
frappe.db.sql("delete from `tabPOS Profile`")
-def get_customers_list(pos_profile={}):
+def get_customers_list(pos_profile=None):
+ if pos_profile is None:
+ pos_profile = {}
cond = "1=1"
customer_groups = []
if pos_profile.get('customer_groups'):
diff --git a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.py b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.py
index d77cdde..404c4ab 100644
--- a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.py
+++ b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class POSProfileUser(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.js b/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.js
deleted file mode 100644
index 5449ab7..0000000
--- a/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Profile User", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new POS Profile User
- () => frappe.tests.make('POS Profile User', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.py b/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.py
index 5c69ab1..dca3556 100644
--- a/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.py
+++ b/erpnext/accounts/doctype/pos_profile_user/test_pos_profile_user.py
@@ -5,5 +5,6 @@
import unittest
+
class TestPOSProfileUser(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/pos_search_fields/pos_search_fields.py b/erpnext/accounts/doctype/pos_search_fields/pos_search_fields.py
index 720ea77..32f9f92 100644
--- a/erpnext/accounts/doctype/pos_search_fields/pos_search_fields.py
+++ b/erpnext/accounts/doctype/pos_search_fields/pos_search_fields.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class POSSearchFields(Document):
pass
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.py b/erpnext/accounts/doctype/pos_settings/pos_settings.py
index 913f498..5c5aaa0 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.py
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.py
@@ -3,9 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class POSSettings(Document):
def validate(self):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/accounts/doctype/pos_settings/test_pos_settings.js b/erpnext/accounts/doctype/pos_settings/test_pos_settings.js
deleted file mode 100644
index 639c94e..0000000
--- a/erpnext/accounts/doctype/pos_settings/test_pos_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: POS Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new POS Settings
- () => frappe.tests.make('POS Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/pos_settings/test_pos_settings.py b/erpnext/accounts/doctype/pos_settings/test_pos_settings.py
index a3df108..949fed7 100644
--- a/erpnext/accounts/doctype/pos_settings/test_pos_settings.py
+++ b/erpnext/accounts/doctype/pos_settings/test_pos_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestPOSSettings(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index 0be41b4..99c5b34 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -2,12 +2,13 @@
"actions": [],
"allow_import": 1,
"allow_rename": 1,
- "autoname": "field:title",
+ "autoname": "naming_series:",
"creation": "2014-02-21 15:02:51",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"applicability_section",
+ "naming_series",
"title",
"disable",
"apply_on",
@@ -95,8 +96,7 @@
"fieldtype": "Data",
"label": "Title",
"no_copy": 1,
- "reqd": 1,
- "unique": 1
+ "reqd": 1
},
{
"default": "0",
@@ -571,6 +571,13 @@
"fieldname": "is_recursive",
"fieldtype": "Check",
"label": "Is Recursive"
+ },
+ {
+ "default": "PRLE-.####",
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Naming Series",
+ "options": "PRLE-.####"
}
],
"icon": "fa fa-gift",
@@ -634,5 +641,6 @@
],
"show_name_in_global_search": 1,
"sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
+ "sort_order": "DESC",
+ "title_field": "title"
+}
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 556f49d..e5bf3b8 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -3,15 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-import json
+
import copy
+import json
import re
-from frappe import throw, _
-from frappe.utils import flt, cint, getdate
+import frappe
+from frappe import _, throw
from frappe.model.document import Document
-
+from frappe.utils import cint, flt, getdate
from six import string_types
apply_on_dict = {"Item Code": "items",
@@ -198,12 +198,19 @@
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)
+ serialized_items = dict()
+ for item_code, val in query_items:
+ serialized_items.setdefault(item_code, val)
+
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)
out.append(data)
- if 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
@@ -221,8 +228,12 @@
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_pricing_rules,
- get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
+ from erpnext.accounts.doctype.pricing_rule.utils import (
+ get_applied_pricing_rules,
+ get_pricing_rule_items,
+ get_pricing_rules,
+ get_product_discount_rule,
+ )
if isinstance(doc, string_types):
doc = json.loads(doc)
@@ -308,9 +319,8 @@
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 TypeError:
- # invalid item_code
- return item_details
+ except frappe.DoesNotExistError:
+ return
if not args.item_group:
frappe.throw(_("Item Group not mentioned in item master for item {0}").format(args.item_code))
@@ -383,8 +393,10 @@
if pricing_rule else args.get(field, 0))
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
- from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
- get_pricing_rule_items)
+ 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)
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 3173db1..23ce4e4 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -3,14 +3,16 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.stock.get_item_details import get_item_details
-from frappe import MandatoryError
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.healthcare.doctype.lab_test_template.lab_test_template import make_item_price
+from erpnext.stock.get_item_details import get_item_details
+
class TestPricingRule(unittest.TestCase):
def setUp(self):
@@ -21,9 +23,10 @@
delete_existing_pricing_rules()
def test_pricing_rule_for_discount(self):
- from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
+ from erpnext.stock.get_item_details import get_item_details
+
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
@@ -103,9 +106,10 @@
self.assertEqual(details.get("discount_percentage"), 15)
def test_pricing_rule_for_margin(self):
- from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
+ from erpnext.stock.get_item_details import get_item_details
+
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
@@ -196,9 +200,10 @@
self.assertEqual(details.get("discount_percentage"), 10)
def test_pricing_rule_for_variants(self):
- from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
+ from erpnext.stock.get_item_details import get_item_details
+
if not frappe.db.exists("Item", "Test Variant PRT"):
frappe.get_doc({
"doctype": "Item",
@@ -615,4 +620,13 @@
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))
\ No newline at end of file
+ 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)
diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js
index 8155e7d..8279b59 100644
--- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js
+++ b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js
@@ -26,4 +26,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 94abf3b..0637fda 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -8,14 +8,14 @@
import copy
import json
-from six import string_types
-
import frappe
+from frappe import _, bold
+from frappe.utils import cint, flt, fmt_money, get_link_to_form, getdate, today
+
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor
-from frappe import _, bold
-from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money
+
class MultiplePricingRuleConflict(frappe.ValidationError): pass
@@ -81,7 +81,7 @@
try:
if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()):
filtered_pricing_rules.append(pricing_rule)
- except:
+ except Exception:
pass
else:
filtered_pricing_rules.append(pricing_rule)
@@ -398,7 +398,9 @@
pricing_rules[0].apply_rule_on_other_items = items
return pricing_rules
-def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
+def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
+ if items is None:
+ items = []
sum_qty, sum_amt = [0, 0]
doctype = doc.get('parenttype') or doc.doctype
@@ -475,7 +477,20 @@
frappe.msgprint(_("User has not applied rule on the invoice {0}")
.format(doc.name))
else:
- doc.set(field, d.get(pr_field))
+ if not d.coupon_code_based:
+ doc.set(field, d.get(pr_field))
+ 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')
+ if coupon_code_pricing_rule == d.name:
+ # if selected coupon code is linked with pricing rule
+ doc.set(field, d.get(pr_field))
+ else:
+ # reset discount if not linked
+ doc.set(field, 0)
+ else:
+ # if coupon code based but no coupon code selected
+ doc.set(field, 0)
doc.calculate_taxes_and_totals()
elif d.price_or_product_discount == 'Product':
diff --git a/erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.py b/erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.py
index e2f70af..5e10b84 100644
--- a/erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.py
+++ b/erpnext/accounts/doctype/pricing_rule_brand/pricing_rule_brand.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PricingRuleBrand(Document):
pass
diff --git a/erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.py b/erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.py
index 3cb7da9..a90ecbb 100644
--- a/erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.py
+++ b/erpnext/accounts/doctype/pricing_rule_detail/pricing_rule_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PricingRuleDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/pricing_rule_item_code/pricing_rule_item_code.py b/erpnext/accounts/doctype/pricing_rule_item_code/pricing_rule_item_code.py
index 4468620..4746b39 100644
--- a/erpnext/accounts/doctype/pricing_rule_item_code/pricing_rule_item_code.py
+++ b/erpnext/accounts/doctype/pricing_rule_item_code/pricing_rule_item_code.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PricingRuleItemCode(Document):
pass
diff --git a/erpnext/accounts/doctype/pricing_rule_item_group/pricing_rule_item_group.py b/erpnext/accounts/doctype/pricing_rule_item_group/pricing_rule_item_group.py
index d3c3619..ff1ba75 100644
--- a/erpnext/accounts/doctype/pricing_rule_item_group/pricing_rule_item_group.py
+++ b/erpnext/accounts/doctype/pricing_rule_item_group/pricing_rule_item_group.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PricingRuleItemGroup(Document):
pass
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 0eac732..72b7b23 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
@@ -3,13 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _
from frappe.model.document import Document
+
+from erpnext.accounts.deferred_revenue import (
+ build_conditions,
+ convert_deferred_expense_to_expense,
+ convert_deferred_revenue_to_income,
+)
from erpnext.accounts.general_ledger import make_reverse_gl_entries
-from erpnext.accounts.deferred_revenue import convert_deferred_expense_to_expense, \
- convert_deferred_revenue_to_income, build_conditions
+
class ProcessDeferredAccounting(Document):
def validate(self):
@@ -31,4 +36,4 @@
'against_voucher': self.name
})
- make_reverse_gl_entries(gl_entries=gl_entries)
\ No newline at end of file
+ make_reverse_gl_entries(gl_entries=gl_entries)
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 e08a0e5..69e2caa 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
@@ -3,11 +3,17 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
from erpnext.accounts.doctype.account.test_account import create_account
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
+ check_gl_entries,
+ create_sales_invoice,
+)
from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice, check_gl_entries
+
class TestProcessDeferredAccounting(unittest.TestCase):
def test_creation_of_ledger_entry_on_submit(self):
@@ -45,4 +51,4 @@
["Sales - _TC", 0.0, 33.85, "2019-01-31"]
]
- check_gl_entries(self, si.name, expected_gle, "2019-01-10")
\ No newline at end of file
+ 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.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
index 295a3b8..a26267b 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
@@ -219,6 +219,7 @@
},
{
"default": "1",
+ "description": "A customer must have primary contact email.",
"fieldname": "primary_mandatory",
"fieldtype": "Check",
"label": "Send To Primary Contact"
@@ -286,10 +287,11 @@
}
],
"links": [],
- "modified": "2021-05-21 11:14:22.426672",
+ "modified": "2021-09-06 21:00:45.732505",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",
+ "naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{
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 500952e..503fd0d 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
@@ -3,22 +3,24 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import copy
+
import frappe
from frappe import _
from frappe.model.document import Document
-from erpnext.accounts.report.general_ledger.general_ledger import execute as get_soa
-from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute as get_ageing
+from frappe.utils import add_days, add_months, format_date, getdate, today
+from frappe.utils.jinja import validate_template
+from frappe.utils.pdf import get_pdf
+from frappe.www.printview import get_print_style
+
from erpnext import get_company_currency
from erpnext.accounts.party import get_party_account_currency
+from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import (
+ execute as get_ageing,
+)
+from erpnext.accounts.report.general_ledger.general_ledger import execute as get_soa
-from frappe.utils.print_format import report_to_pdf
-from frappe.utils.pdf import get_pdf
-from frappe.utils import today, add_days, add_months, getdate, format_date
-from frappe.utils.jinja import validate_template
-
-import copy
-from datetime import timedelta
-from frappe.www.printview import get_print_style
class ProcessStatementOfAccounts(Document):
def validate(self):
@@ -158,7 +160,7 @@
if doc.cc_to != '':
try:
cc=[frappe.get_value('User', doc.cc_to, 'email')]
- except:
+ except Exception:
pass
return recipients, cc
@@ -194,7 +196,10 @@
primary_email = customer.get('email_id') or ''
billing_email = get_customer_emails(customer.name, 1, billing_and_primary=False)
- if billing_email == '' or (primary_email == '' and int(primary_mandatory)):
+ if int(primary_mandatory):
+ if (primary_email == ''):
+ continue
+ elif (billing_email == '') and (primary_email == ''):
continue
customer_list.append({
@@ -206,10 +211,29 @@
@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 """
+
billing_email = frappe.db.sql("""
- SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent
- WHERE l.link_doctype='Customer' and l.link_name=%s and c.is_billing_contact=1
- order by c.creation desc""", customer_name)
+ SELECT
+ email.email_id
+ FROM
+ `tabContact Email` AS email
+ JOIN
+ `tabDynamic Link` AS link
+ ON
+ email.parent=link.parent
+ JOIN
+ `tabContact` AS contact
+ ON
+ contact.name=link.parent
+ WHERE
+ link.link_doctype='Customer'
+ and link.link_name=%s
+ and contact.is_billing_contact=1
+ ORDER BY
+ contact.creation desc""", customer_name)
if len(billing_email) == 0 or (billing_email[0][0] is None):
if billing_and_primary:
@@ -284,4 +308,4 @@
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
\ No newline at end of file
+ return True
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
index 30efbb3..7ddcd10 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestProcessStatementOfAccounts(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py
index 1a76010..fe94009 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProcessStatementOfAccountsCustomer(Document):
pass
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js
index 890a187..e840c79 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js
@@ -48,4 +48,4 @@
frm.doc.apply_on === key ? 1 : 0);
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
index cc71324..1d68b23 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json
@@ -1,1381 +1,339 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "Prompt",
- "beta": 0,
"creation": "2019-02-08 17:10:36.077402",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "section_break_1",
+ "apply_on",
+ "disable",
+ "column_break_3",
+ "items",
+ "item_groups",
+ "brands",
+ "mixed_conditions",
+ "is_cumulative",
+ "section_break_10",
+ "apply_rule_on_other",
+ "column_break_11",
+ "other_item_code",
+ "other_item_group",
+ "other_brand",
+ "section_break_8",
+ "selling",
+ "buying",
+ "column_break_12",
+ "applicable_for",
+ "customer",
+ "customer_group",
+ "territory",
+ "sales_partner",
+ "campaign",
+ "supplier",
+ "supplier_group",
+ "period_settings_section",
+ "valid_from",
+ "valid_upto",
+ "column_break_26",
+ "company",
+ "currency",
+ "section_break_14",
+ "price_discount_slabs",
+ "section_break_15",
+ "product_discount_slabs"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "section_break_1",
- "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": "",
- "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
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Item Code",
- "fetch_if_empty": 0,
"fieldname": "apply_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": "Apply On",
- "length": 0,
- "no_copy": 0,
"options": "\nItem Code\nItem Group\nBrand\nTransaction",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "default": "0",
"fieldname": "disable",
"fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable",
- "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
+ "label": "Disable"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "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
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.apply_on == 'Item Code'",
- "fetch_if_empty": 0,
"fieldname": "items",
"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": "Pricing Rule Item Code",
- "length": 0,
- "no_copy": 0,
- "options": "Pricing Rule Item Code",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Pricing Rule Item Code"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.apply_on == 'Item Group'",
- "fetch_if_empty": 0,
"fieldname": "item_groups",
"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": "Pricing Rule Item Group",
- "length": 0,
- "no_copy": 0,
- "options": "Pricing Rule Item 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,
- "translatable": 0,
- "unique": 0
+ "options": "Pricing Rule Item Group"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.apply_on == 'Brand'",
- "fetch_if_empty": 0,
"fieldname": "brands",
"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": "Pricing Rule Brand",
- "length": 0,
- "no_copy": 0,
- "options": "Pricing Rule Brand",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Pricing Rule Brand"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "default": "0",
"fieldname": "mixed_conditions",
"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": "Mixed Conditions",
- "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
+ "label": "Mixed Conditions"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "default": "0",
"fieldname": "is_cumulative",
"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": "Is Cumulative",
- "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
+ "label": "Is Cumulative"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "section_break_10",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Discount on Other Item",
- "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
+ "label": "Discount on Other Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "apply_rule_on_other",
"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": "Apply Rule On Other",
- "length": 0,
- "no_copy": 0,
- "options": "\nItem Code\nItem Group\nBrand",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "\nItem Code\nItem Group\nBrand"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "column_break_11",
- "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
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.apply_rule_on_other == 'Item Code'",
- "fetch_if_empty": 0,
"fieldname": "other_item_code",
"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": "Item Code",
- "length": 0,
- "no_copy": 0,
- "options": "Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.apply_rule_on_other == 'Item Group'",
- "fetch_if_empty": 0,
"fieldname": "other_item_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": "Item Group",
- "length": 0,
- "no_copy": 0,
- "options": "Item 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,
- "translatable": 0,
- "unique": 0
+ "options": "Item Group"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.apply_rule_on_other == 'Brand'",
- "fetch_if_empty": 0,
"fieldname": "other_brand",
"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": "Brand",
- "length": 0,
- "no_copy": 0,
- "options": "Brand",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Brand"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 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": "Party Information",
- "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
+ "label": "Party Information"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "default": "0",
"fieldname": "selling",
"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": "Selling",
- "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
+ "label": "Selling"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "default": "0",
"fieldname": "buying",
"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": "Buying",
- "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
+ "label": "Buying"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval: doc.buying || doc.selling",
- "fetch_if_empty": 0,
"fieldname": "applicable_for",
"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": "Applicable For",
- "length": 0,
- "no_copy": 0,
- "options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier 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,
- "translatable": 0,
- "unique": 0
+ "options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Group"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for=='Customer'",
- "fetch_if_empty": 0,
"fieldname": "customer",
- "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,
+ "fieldtype": "Table MultiSelect",
"label": "Customer",
- "length": 0,
- "no_copy": 0,
- "options": "Customer",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Customer Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for==\"Customer Group\"",
- "fetch_if_empty": 0,
"fieldname": "customer_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,
+ "fieldtype": "Table MultiSelect",
"label": "Customer Group",
- "length": 0,
- "no_copy": 0,
- "options": "Customer 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,
- "translatable": 0,
- "unique": 0
+ "options": "Customer Group Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for==\"Territory\"",
- "fetch_if_empty": 0,
"fieldname": "territory",
- "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,
+ "fieldtype": "Table MultiSelect",
"label": "Territory",
- "length": 0,
- "no_copy": 0,
- "options": "Territory",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Territory Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for==\"Sales Partner\"",
- "fetch_if_empty": 0,
"fieldname": "sales_partner",
- "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,
+ "fieldtype": "Table MultiSelect",
"label": "Sales Partner",
- "length": 0,
- "no_copy": 0,
- "options": "Sales Partner",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Sales Partner Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for==\"Campaign\"",
- "fetch_if_empty": 0,
"fieldname": "campaign",
- "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,
+ "fieldtype": "Table MultiSelect",
"label": "Campaign",
- "length": 0,
- "no_copy": 0,
- "options": "Campaign",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Campaign Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for=='Supplier'",
- "fetch_if_empty": 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,
+ "fieldtype": "Table MultiSelect",
"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,
- "translatable": 0,
- "unique": 0
+ "options": "Supplier Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.applicable_for==\"Supplier Group\"",
- "fetch_if_empty": 0,
"fieldname": "supplier_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,
+ "fieldtype": "Table MultiSelect",
"label": "Supplier Group",
- "length": 0,
- "no_copy": 0,
- "options": "Supplier 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,
- "translatable": 0,
- "unique": 0
+ "options": "Supplier Group Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "period_settings_section",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Period Settings",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Period Settings"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Today",
- "fetch_if_empty": 0,
"fieldname": "valid_from",
"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": "Valid From",
- "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
+ "label": "Valid From"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "valid_upto",
"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": "Valid Upto",
- "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
+ "label": "Valid Upto"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "column_break_26",
- "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
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Company",
- "length": 0,
- "no_copy": 0,
"options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "currency",
"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": "Currency",
- "length": 0,
- "no_copy": 0,
- "options": "Currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Currency"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "section_break_14",
"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": "Price Discount Slabs",
- "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
+ "label": "Price Discount Slabs"
},
{
"allow_bulk_edit": 1,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "price_discount_slabs",
"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": "Promotional Scheme Price Discount",
- "length": 0,
- "no_copy": 0,
- "options": "Promotional Scheme Price Discount",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Promotional Scheme Price Discount"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
"fieldname": "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,
- "label": "Product Discount Slabs",
- "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
+ "label": "Product Discount Slabs"
},
{
"allow_bulk_edit": 1,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "product_discount_slabs",
"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": "Promotional Scheme Product Discount",
- "length": 0,
- "no_copy": 0,
- "options": "Promotional Scheme Product Discount",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Promotional Scheme Product Discount"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-03-25 12:14:27.486586",
+ "links": [],
+ "modified": "2021-05-06 16:20:22.039078",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Promotional Scheme",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
index 7d93023..f5391ca 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
@@ -3,10 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cstr
-from frappe.model.naming import make_autoname
from frappe.model.document import Document
pricing_rule_fields = ['apply_on', 'mixed_conditions', 'is_cumulative', 'other_item_code', 'other_item_group',
@@ -25,22 +24,31 @@
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):
frappe.throw(_("Price or product discount slabs are required"))
def on_update(self):
- data = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"],
- filters = {'promotional_scheme': self.name}) 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)
- self.update_pricing_rules(data)
-
- def update_pricing_rules(self, data):
+ def update_pricing_rules(self, pricing_rules):
rules = {}
count = 0
-
- for d in data:
- rules[d.get('promotional_scheme_id')] = d.get('name')
+ names = []
+ for rule in pricing_rules:
+ names.append(rule.name)
+ rules[rule.get('promotional_scheme_id')] = names
docs = get_pricing_rules(self, rules)
@@ -57,11 +65,13 @@
frappe.msgprint(_("New {0} pricing rules are created").format(count))
def on_trash(self):
- for d in frappe.get_all('Pricing Rule',
+ for rule in frappe.get_all('Pricing Rule',
{'promotional_scheme': self.name}):
- frappe.delete_doc('Pricing Rule', d.name)
+ frappe.delete_doc('Pricing Rule', rule.name)
-def get_pricing_rules(doc, rules = {}):
+def get_pricing_rules(doc, rules=None):
+ if rules is None:
+ rules = {}
new_doc = []
for child_doc, fields in {'price_discount_slabs': price_discount_fields,
'product_discount_slabs': product_discount_fields}.items():
@@ -70,45 +80,85 @@
return new_doc
-def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}):
+def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
+ if rules is None:
+ rules = {}
new_doc = []
args = get_args_for_pricing_rule(doc)
- for d in doc.get(child_doc):
+ applicable_for = frappe.scrub(doc.get('applicable_for'))
+ for idx, d in enumerate(doc.get(child_doc)):
if d.name in rules:
- pr = frappe.get_doc('Pricing Rule', rules.get(d.name))
+ for applicable_for_value in args.get(applicable_for):
+ temp_args = args.copy()
+ docname = frappe.get_all(
+ 'Pricing Rule',
+ fields = ["promotional_scheme_id", "name", applicable_for],
+ filters = {
+ 'promotional_scheme_id': d.name,
+ applicable_for: applicable_for_value
+ }
+ )
+
+ if docname:
+ pr = frappe.get_doc('Pricing Rule', docname[0].get('name'))
+ temp_args[applicable_for] = applicable_for_value
+ pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d)
+ else:
+ pr = frappe.new_doc("Pricing Rule")
+ pr.title = doc.name
+ temp_args[applicable_for] = applicable_for_value
+ pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d)
+
+ new_doc.append(pr)
+
else:
- pr = frappe.new_doc("Pricing Rule")
- pr.title = make_autoname("{0}/.####".format(doc.name))
-
- pr.update(args)
- for field in (other_fields + discount_fields):
- pr.set(field, d.get(field))
-
- pr.promotional_scheme_id = d.name
- pr.promotional_scheme = doc.name
- pr.disable = d.disable if d.disable else doc.disable
- pr.price_or_product_discount = ('Price'
- if child_doc == 'price_discount_slabs' else 'Product')
-
- for field in ['items', 'item_groups', 'brands']:
- if doc.get(field):
- pr.set(field, [])
-
- 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
- })
-
- new_doc.append(pr)
+ applicable_for_values = args.get(applicable_for) or []
+ for applicable_for_value in applicable_for_values:
+ pr = frappe.new_doc("Pricing Rule")
+ pr.title = doc.name
+ temp_args = args.copy()
+ temp_args[applicable_for] = applicable_for_value
+ pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d)
+ new_doc.append(pr)
return new_doc
+
+
+
+def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields):
+ pr.update(args)
+ 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')
+
+ for field in ['items', 'item_groups', 'brands']:
+ if doc.get(field):
+ pr.set(field, [])
+
+ 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
+ })
+ return pr
+
def get_args_for_pricing_rule(doc):
args = { 'promotional_scheme': doc.name }
+ applicable_for = frappe.scrub(doc.get('applicable_for'))
for d in pricing_rule_fields:
- args[d] = doc.get(d)
-
+ if d == applicable_for:
+ items = []
+ for applicable_for_values in doc.get(applicable_for):
+ items.append(applicable_for_values.get(applicable_for))
+ args[d] = items
+ else:
+ args[d] = doc.get(d)
return args
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
index 28c4c61..6d07924 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py
@@ -1,5 +1,6 @@
from frappe import _
+
def get_data():
return {
'fieldname': 'promotional_scheme',
@@ -9,4 +10,4 @@
'items': ['Pricing Rule']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
index 8dc0499..190b734 100644
--- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
@@ -3,8 +3,60 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestPromotionalScheme(unittest.TestCase):
- pass
+ def test_promotional_scheme(self):
+ ps = make_promotional_scheme()
+ 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.save()
+ 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')
+ 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')
+ 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})
+ self.assertEqual(price_rules, [])
+
+def make_promotional_scheme():
+ 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,
+ 'discount_percentage': 20,
+ 'rule_description': 'Test'
+ })
+ ps.applicable_for = 'Customer'
+ ps.append('customer',{
+ 'customer': "_Test Customer"
+ })
+ ps.save()
+
+ return ps
diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
index 795fb1c..a70d5c9 100644
--- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
+++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
@@ -106,7 +106,6 @@
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
"fieldname": "rate",
"fieldtype": "Currency",
- "in_list_view": 1,
"label": "Rate"
},
{
@@ -170,7 +169,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-03-07 11:56:23.424137",
+ "modified": "2021-08-19 15:49:29.598727",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Promotional Scheme Price Discount",
diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py
index 380ae32..ab8efc3 100644
--- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py
+++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PromotionalSchemePriceDiscount(Document):
pass
diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py
index 2931106..85019b4 100644
--- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py
+++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PromotionalSchemeProductDiscount(Document):
pass
diff --git a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py
index 0aeef3e..cb5aaa9 100644
--- a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py
+++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class PSOACostCenter(Document):
pass
diff --git a/erpnext/accounts/doctype/psoa_project/psoa_project.py b/erpnext/accounts/doctype/psoa_project/psoa_project.py
index f4a5dee..1cd9951 100644
--- a/erpnext/accounts/doctype/psoa_project/psoa_project.py
+++ b/erpnext/accounts/doctype/psoa_project/psoa_project.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class PSOAProject(Document):
pass
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 79ea017..6c74d2b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -275,7 +275,7 @@
// Do not update if inter company reference is there as the details will already be updated
if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
return;
-
+
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{
posting_date: this.frm.doc.posting_date,
@@ -591,4 +591,4 @@
company: function(frm) {
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
},
-})
\ No newline at end of file
+})
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 7025dd9..55e288e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -177,9 +177,7 @@
"hidden": 1,
"label": "Title",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "naming_series",
@@ -191,9 +189,7 @@
"options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-",
"print_hide": 1,
"reqd": 1,
- "set_only_once": 1,
- "show_days": 1,
- "show_seconds": 1
+ "set_only_once": 1
},
{
"fieldname": "supplier",
@@ -205,9 +201,7 @@
"options": "Supplier",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"bold": 1,
@@ -219,9 +213,7 @@
"label": "Supplier Name",
"oldfieldname": "supplier_name",
"oldfieldtype": "Data",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fetch_from": "supplier.tax_id",
@@ -229,27 +221,21 @@
"fieldtype": "Read Only",
"label": "Tax Id",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "due_date",
"fieldtype": "Date",
"label": "Due Date",
"oldfieldname": "due_date",
- "oldfieldtype": "Date",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Date"
},
{
"default": "0",
"fieldname": "is_paid",
"fieldtype": "Check",
"label": "Is Paid",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -257,25 +243,19 @@
"fieldtype": "Check",
"label": "Is Return (Debit Note)",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
"fieldname": "apply_tds",
"fieldtype": "Check",
"label": "Apply Tax Withholding Amount",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -285,17 +265,13 @@
"label": "Company",
"options": "Company",
"print_hide": 1,
- "remember_last_selected_value": 1,
- "show_days": 1,
- "show_seconds": 1
+ "remember_last_selected_value": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
- "options": "Cost Center",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Cost Center"
},
{
"default": "Today",
@@ -307,9 +283,7 @@
"oldfieldtype": "Date",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"fieldname": "posting_time",
@@ -318,8 +292,6 @@
"no_copy": 1,
"print_hide": 1,
"print_width": "100px",
- "show_days": 1,
- "show_seconds": 1,
"width": "100px"
},
{
@@ -328,9 +300,7 @@
"fieldname": "set_posting_time",
"fieldtype": "Check",
"label": "Edit Posting Date and Time",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "amended_from",
@@ -342,58 +312,44 @@
"oldfieldtype": "Link",
"options": "Purchase Invoice",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "eval:doc.on_hold",
"fieldname": "sb_14",
"fieldtype": "Section Break",
- "label": "Hold Invoice",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Hold Invoice"
},
{
"default": "0",
"fieldname": "on_hold",
"fieldtype": "Check",
- "label": "Hold Invoice",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Hold Invoice"
},
{
"depends_on": "eval:doc.on_hold",
"description": "Once set, this invoice will be on hold till the set date",
"fieldname": "release_date",
"fieldtype": "Date",
- "label": "Release Date",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Release Date"
},
{
"fieldname": "cb_17",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"depends_on": "eval:doc.on_hold",
"fieldname": "hold_comment",
"fieldtype": "Small Text",
- "label": "Reason For Putting On Hold",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Reason For Putting On Hold"
},
{
"collapsible": 1,
"collapsible_depends_on": "bill_no",
"fieldname": "supplier_invoice_details",
"fieldtype": "Section Break",
- "label": "Supplier Invoice Details",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Supplier Invoice Details"
},
{
"fieldname": "bill_no",
@@ -401,15 +357,11 @@
"label": "Supplier Invoice No",
"oldfieldname": "bill_no",
"oldfieldtype": "Data",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_15",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "bill_date",
@@ -418,17 +370,13 @@
"no_copy": 1,
"oldfieldname": "bill_date",
"oldfieldtype": "Date",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "return_against",
"fieldname": "returns",
"fieldtype": "Section Break",
- "label": "Returns",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Returns"
},
{
"depends_on": "return_against",
@@ -438,34 +386,26 @@
"no_copy": 1,
"options": "Purchase Invoice",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"fieldname": "section_addresses",
"fieldtype": "Section Break",
- "label": "Address and Contact",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Address and Contact"
},
{
"fieldname": "supplier_address",
"fieldtype": "Link",
"label": "Select Supplier Address",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "address_display",
"fieldtype": "Small Text",
"label": "Address",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "contact_person",
@@ -473,67 +413,51 @@
"in_global_search": 1,
"label": "Contact Person",
"options": "Contact",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "contact_display",
"fieldtype": "Small Text",
"label": "Contact",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "contact_mobile",
"fieldtype": "Small Text",
"label": "Mobile No",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "contact_email",
"fieldtype": "Small Text",
"label": "Contact Email",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "col_break_address",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "shipping_address",
"fieldtype": "Link",
"label": "Select Shipping Address",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "shipping_address_display",
"fieldtype": "Small Text",
"label": "Shipping Address",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"fieldname": "currency_and_price_list",
"fieldtype": "Section Break",
"label": "Currency and Price List",
- "options": "fa fa-tag",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-tag"
},
{
"fieldname": "currency",
@@ -542,9 +466,7 @@
"oldfieldname": "currency",
"oldfieldtype": "Select",
"options": "Currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "conversion_rate",
@@ -553,24 +475,18 @@
"oldfieldname": "conversion_rate",
"oldfieldtype": "Currency",
"precision": "9",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break2",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "buying_price_list",
"fieldtype": "Link",
"label": "Price List",
"options": "Price List",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "price_list_currency",
@@ -578,18 +494,14 @@
"label": "Price List Currency",
"options": "Currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "plc_conversion_rate",
"fieldtype": "Float",
"label": "Price List Exchange Rate",
"precision": "9",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -598,15 +510,11 @@
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "sec_warehouse",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"depends_on": "update_stock",
@@ -615,9 +523,7 @@
"fieldtype": "Link",
"label": "Set Accepted Warehouse",
"options": "Warehouse",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "update_stock",
@@ -627,15 +533,11 @@
"label": "Rejected Warehouse",
"no_copy": 1,
"options": "Warehouse",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "col_break_warehouse",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"default": "No",
@@ -643,33 +545,26 @@
"fieldtype": "Select",
"label": "Raw Materials Supplied",
"options": "No\nYes",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-shopping-cart"
},
{
"default": "0",
"fieldname": "update_stock",
"fieldtype": "Check",
"label": "Update Stock",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
"label": "Scan Barcode",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -679,33 +574,25 @@
"oldfieldname": "entries",
"oldfieldtype": "Table",
"options": "Purchase Invoice Item",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "pricing_rule_details",
"fieldtype": "Section Break",
- "label": "Pricing Rules",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Pricing Rules"
},
{
"fieldname": "pricing_rules",
"fieldtype": "Table",
"label": "Pricing Rule Detail",
"options": "Pricing Rule Detail",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible_depends_on": "supplied_items",
"fieldname": "raw_materials_supplied",
"fieldtype": "Section Break",
- "label": "Raw Materials Supplied",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Raw Materials Supplied"
},
{
"depends_on": "update_stock",
@@ -713,23 +600,17 @@
"fieldtype": "Table",
"label": "Supplied Items",
"no_copy": 1,
- "options": "Purchase Receipt Item Supplied",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Purchase Receipt Item Supplied"
},
{
"fieldname": "section_break_26",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"fieldname": "total_qty",
"fieldtype": "Float",
"label": "Total Quantity",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_total",
@@ -737,9 +618,7 @@
"label": "Total (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_net_total",
@@ -749,24 +628,18 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_28",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "total",
"fieldtype": "Currency",
"label": "Total",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "net_total",
@@ -776,56 +649,42 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "total_net_weight",
"fieldtype": "Float",
"label": "Total Net Weight",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "taxes_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-money",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-money"
},
{
"fieldname": "tax_category",
"fieldtype": "Link",
"label": "Tax Category",
"options": "Tax Category",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_49",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "shipping_rule",
"fieldtype": "Link",
"label": "Shipping Rule",
"options": "Shipping Rule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "section_break_51",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"fieldname": "taxes_and_charges",
@@ -834,9 +693,7 @@
"oldfieldname": "purchase_other_charges",
"oldfieldtype": "Link",
"options": "Purchase Taxes and Charges Template",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "taxes",
@@ -844,17 +701,13 @@
"label": "Purchase Taxes and Charges",
"oldfieldname": "purchase_tax_details",
"oldfieldtype": "Table",
- "options": "Purchase Taxes and Charges",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Purchase Taxes and Charges"
},
{
"collapsible": 1,
"fieldname": "sec_tax_breakup",
"fieldtype": "Section Break",
- "label": "Tax Breakup",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Tax Breakup"
},
{
"fieldname": "other_charges_calculation",
@@ -863,17 +716,13 @@
"no_copy": 1,
"oldfieldtype": "HTML",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "totals",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-money",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-money"
},
{
"fieldname": "base_taxes_and_charges_added",
@@ -883,9 +732,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_taxes_and_charges_deducted",
@@ -895,9 +742,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_total_taxes_and_charges",
@@ -907,15 +752,11 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_40",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "taxes_and_charges_added",
@@ -925,9 +766,7 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "taxes_and_charges_deducted",
@@ -937,9 +776,7 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "total_taxes_and_charges",
@@ -947,18 +784,14 @@
"label": "Total Taxes and Charges",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_44",
"fieldtype": "Section Break",
- "label": "Additional Discount",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Additional Discount"
},
{
"default": "Grand Total",
@@ -966,9 +799,7 @@
"fieldtype": "Select",
"label": "Apply Additional Discount On",
"options": "\nGrand Total\nNet Total",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "base_discount_amount",
@@ -976,38 +807,28 @@
"label": "Additional Discount Amount (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_46",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "additional_discount_percentage",
"fieldtype": "Float",
"label": "Additional Discount Percentage",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "discount_amount",
"fieldtype": "Currency",
"label": "Additional Discount Amount",
"options": "currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "section_break_49",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"fieldname": "base_grand_total",
@@ -1017,9 +838,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1029,9 +848,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1041,9 +858,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_in_words",
@@ -1053,17 +868,13 @@
"oldfieldname": "in_words",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break8",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"print_hide": 1,
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -1074,9 +885,7 @@
"oldfieldname": "grand_total_import",
"oldfieldtype": "Currency",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1086,9 +895,7 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1098,9 +905,7 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "in_words",
@@ -1110,9 +915,7 @@
"oldfieldname": "in_words_import",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "total_advance",
@@ -1123,9 +926,7 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "outstanding_amount",
@@ -1136,18 +937,14 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "0",
"depends_on": "grand_total",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
- "label": "Disable Rounded Total",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Disable Rounded Total"
},
{
"collapsible": 1,
@@ -1155,26 +952,20 @@
"depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)",
"fieldname": "payments_section",
"fieldtype": "Section Break",
- "label": "Payments",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Payments"
},
{
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"label": "Mode of Payment",
"options": "Mode of Payment",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "cash_bank_account",
"fieldtype": "Link",
"label": "Cash/Bank Account",
- "options": "Account",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Account"
},
{
"fieldname": "clearance_date",
@@ -1182,15 +973,11 @@
"label": "Clearance Date",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "col_br_payments",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"depends_on": "is_paid",
@@ -1199,9 +986,7 @@
"label": "Paid Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "base_paid_amount",
@@ -1210,9 +995,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
@@ -1220,9 +1003,7 @@
"depends_on": "grand_total",
"fieldname": "write_off",
"fieldtype": "Section Break",
- "label": "Write Off",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Write Off"
},
{
"fieldname": "write_off_amount",
@@ -1230,9 +1011,7 @@
"label": "Write Off Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "base_write_off_amount",
@@ -1241,15 +1020,11 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_61",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"depends_on": "eval:flt(doc.write_off_amount)!=0",
@@ -1257,9 +1032,7 @@
"fieldtype": "Link",
"label": "Write Off Account",
"options": "Account",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "eval:flt(doc.write_off_amount)!=0",
@@ -1267,9 +1040,7 @@
"fieldtype": "Link",
"label": "Write Off Cost Center",
"options": "Cost Center",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1279,17 +1050,13 @@
"label": "Advance Payments",
"oldfieldtype": "Section Break",
"options": "fa fa-money",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
"fieldname": "allocate_advances_automatically",
"fieldtype": "Check",
- "label": "Set Advances and Allocate (FIFO)",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Set Advances and Allocate (FIFO)"
},
{
"depends_on": "eval:!doc.allocate_advances_automatically",
@@ -1297,9 +1064,7 @@
"fieldtype": "Button",
"label": "Get Advances Paid",
"oldfieldtype": "Button",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "advances",
@@ -1309,26 +1074,20 @@
"oldfieldname": "advance_allocation_details",
"oldfieldtype": "Table",
"options": "Purchase Invoice Advance",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "eval:(!doc.is_return)",
"fieldname": "payment_schedule_section",
"fieldtype": "Section Break",
- "label": "Payment Terms",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Payment Terms"
},
{
"fieldname": "payment_terms_template",
"fieldtype": "Link",
"label": "Payment Terms Template",
- "options": "Payment Terms Template",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Payment Terms Template"
},
{
"fieldname": "payment_schedule",
@@ -1336,9 +1095,7 @@
"label": "Payment Schedule",
"no_copy": 1,
"options": "Payment Schedule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1346,33 +1103,25 @@
"fieldname": "terms_section_break",
"fieldtype": "Section Break",
"label": "Terms and Conditions",
- "options": "fa fa-legal",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-legal"
},
{
"fieldname": "tc_name",
"fieldtype": "Link",
"label": "Terms",
"options": "Terms and Conditions",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "terms",
"fieldtype": "Text Editor",
- "label": "Terms and Conditions1",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Terms and Conditions1"
},
{
"collapsible": 1,
"fieldname": "printing_settings",
"fieldtype": "Section Break",
- "label": "Printing Settings",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Printing Settings"
},
{
"allow_on_submit": 1,
@@ -1380,9 +1129,7 @@
"fieldtype": "Link",
"label": "Letter Head",
"options": "Letter Head",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1390,15 +1137,11 @@
"fieldname": "group_same_items",
"fieldtype": "Check",
"label": "Group same items",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_112",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
@@ -1410,18 +1153,14 @@
"oldfieldtype": "Link",
"options": "Print Heading",
"print_hide": 1,
- "report_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "report_hide": 1
},
{
"fieldname": "language",
"fieldtype": "Data",
"label": "Print Language",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
@@ -1430,9 +1169,7 @@
"label": "More Information",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "credit_to",
@@ -1443,9 +1180,7 @@
"options": "Account",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"fieldname": "party_account_currency",
@@ -1455,9 +1190,7 @@
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "No",
@@ -1467,9 +1200,7 @@
"oldfieldname": "is_opening",
"oldfieldtype": "Select",
"options": "No\nYes",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "against_expense_account",
@@ -1479,15 +1210,11 @@
"no_copy": 1,
"oldfieldname": "against_expense_account",
"oldfieldtype": "Small Text",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_63",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"default": "Draft",
@@ -1495,10 +1222,8 @@
"fieldtype": "Select",
"in_standard_filter": 1,
"label": "Status",
- "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
+ "print_hide": 1
},
{
"fieldname": "inter_company_invoice_reference",
@@ -1507,9 +1232,7 @@
"no_copy": 1,
"options": "Sales Invoice",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "remarks",
@@ -1518,18 +1241,14 @@
"no_copy": 1,
"oldfieldname": "remarks",
"oldfieldtype": "Text",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"label": "Subscription Section",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1538,9 +1257,7 @@
"fieldtype": "Date",
"label": "From Date",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1549,15 +1266,11 @@
"fieldtype": "Date",
"label": "To Date",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_114",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "auto_repeat",
@@ -1566,42 +1279,33 @@
"no_copy": 1,
"options": "Auto Repeat",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval: doc.auto_repeat",
"fieldname": "update_auto_repeat_reference",
"fieldtype": "Button",
- "label": "Update Auto Repeat Reference",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Update Auto Repeat Reference"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
- "label": "Accounting Dimensions ",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Accounting Dimensions "
},
{
"fieldname": "dimension_col_break",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"default": "0",
"fetch_from": "supplier.is_internal_supplier",
"fieldname": "is_internal_supplier",
"fieldtype": "Check",
+ "ignore_user_permissions": 1,
"label": "Is Internal Supplier",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "tax_withholding_category",
@@ -1609,33 +1313,25 @@
"hidden": 1,
"label": "Tax Withholding Category",
"options": "Tax Withholding Category",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "billing_address",
"fieldtype": "Link",
"label": "Select Billing Address",
- "options": "Address",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Address"
},
{
"fieldname": "billing_address_display",
"fieldtype": "Small Text",
"label": "Billing Address",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
- "options": "Project",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Project"
},
{
"depends_on": "eval:doc.is_internal_supplier",
@@ -1643,9 +1339,7 @@
"fieldname": "unrealized_profit_loss_account",
"fieldtype": "Link",
"label": "Unrealized Profit / Loss Account",
- "options": "Account",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Account"
},
{
"depends_on": "eval:doc.is_internal_supplier",
@@ -1654,9 +1348,7 @@
"fieldname": "represents_company",
"fieldtype": "Link",
"label": "Represents Company",
- "options": "Company",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Company"
},
{
"depends_on": "eval:doc.update_stock && doc.is_internal_supplier",
@@ -1668,8 +1360,6 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "50px",
- "show_days": 1,
- "show_seconds": 1,
"width": "50px"
},
{
@@ -1681,8 +1371,6 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "50px",
- "show_days": 1,
- "show_seconds": 1,
"width": "50px"
},
{
@@ -1706,20 +1394,19 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Ignore Default Payment Terms Template",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2021-08-07 17:53:14.351439",
+ "modified": "2021-09-28 13:10:28.351810",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index c3cb159..1c9943f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -3,29 +3,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate, get_link_to_form
-from frappe import _, throw
-import frappe.defaults
-from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
-from erpnext.controllers.buying_controller import BuyingController
-from erpnext.accounts.party import get_party_account, get_due_date
-from erpnext.accounts.utils import get_account_currency, get_fiscal_year
-from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
-from erpnext.stock import get_warehouse_account_map
-from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, make_reverse_gl_entries
-from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
-from erpnext.buying.utils import check_on_hold_or_closed_status
-from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
-from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
+import frappe
+from frappe import _, throw
from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
from six import iteritems
-from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
- unlink_inter_company_doc, check_if_return_invoice_linked_with_payment_entry
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
+
+import erpnext
from erpnext.accounts.deferred_revenue import validate_service_stop_date
-from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost
+from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+ check_if_return_invoice_linked_with_payment_entry,
+ is_overdue,
+ unlink_inter_company_doc,
+ update_linked_doc,
+ validate_inter_company_party,
+)
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
+ get_party_tax_withholding_details,
+)
+from erpnext.accounts.general_ledger import (
+ get_round_off_account_and_cost_center,
+ make_gl_entries,
+ make_reverse_gl_entries,
+ merge_similar_entries,
+)
+from erpnext.accounts.party import get_due_date, get_party_account
+from erpnext.accounts.utils import get_account_currency, get_fiscal_year
+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.buying_controller import BuyingController
+from erpnext.stock import get_warehouse_account_map
+from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+ get_item_account_wise_additional_cost,
+ update_billed_amount_based_on_po,
+)
+
class WarehouseMissingError(frappe.ValidationError): pass
@@ -415,6 +430,8 @@
self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
+ self.process_common_party_accounting()
+
def make_gl_entries(self, gl_entries=None, from_repost=False):
if not gl_entries:
gl_entries = self.get_gl_entries()
@@ -522,7 +539,9 @@
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'))
+
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
@@ -613,7 +632,7 @@
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, self.enable_discount_accounting)
+ dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
@@ -855,9 +874,10 @@
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'))
for tax in self.get("taxes"):
- amount, base_amount = self.get_tax_amounts(tax, self.enable_discount_accounting)
+ amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
if tax.category in ("Total", "Valuation and Total") and flt(base_amount):
account_currency = get_account_currency(tax.account_head)
@@ -1126,6 +1146,12 @@
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 not self.tax_withholding_category:
+ return
+
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
if not tax_withholding_details:
@@ -1156,10 +1182,7 @@
self.status = 'Draft'
return
- precision = self.precision("outstanding_amount")
- outstanding_amount = flt(self.outstanding_amount, precision)
- due_date = getdate(self.due_date)
- nowdate = getdate()
+ outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
if not status:
if self.docstatus == 2:
@@ -1167,9 +1190,11 @@
elif self.docstatus == 1:
if self.is_internal_transfer():
self.status = 'Internal Transfer'
- elif outstanding_amount > 0 and due_date < nowdate:
+ elif is_overdue(self):
self.status = "Overdue"
- elif outstanding_amount > 0 and due_date >= nowdate:
+ elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_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}):
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
index 173939d..4cc319d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'purchase_invoice',
@@ -34,4 +36,4 @@
'items': ['Auto Repeat']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
index 914a245..f6ff83a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
@@ -2,28 +2,58 @@
// License: GNU General Public License v3. See license.txt
// render
-frappe.listview_settings['Purchase Invoice'] = {
- add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
- "currency", "is_return", "release_date", "on_hold", "represents_company", "is_internal_supplier"],
- get_indicator: function(doc) {
- if ((flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
- return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
- } else if (flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
- if(cint(doc.on_hold) && !doc.release_date) {
- return [__("On Hold"), "darkgrey"];
- } else if (cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
- return [__("Temporarily on Hold"), "darkgrey"];
- } else if (frappe.datetime.get_diff(doc.due_date) < 0) {
- return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
- } else {
- return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
- }
- } else if (cint(doc.is_return)) {
- return [__("Return"), "gray", "is_return,=,Yes"];
- } else if (doc.company == doc.represents_company && doc.is_internal_supplier) {
- return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"];
- } else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
- return [__("Paid"), "green", "outstanding_amount,=,0"];
+frappe.listview_settings["Purchase Invoice"] = {
+ add_fields: [
+ "supplier",
+ "supplier_name",
+ "base_grand_total",
+ "outstanding_amount",
+ "due_date",
+ "company",
+ "currency",
+ "is_return",
+ "release_date",
+ "on_hold",
+ "represents_company",
+ "is_internal_supplier",
+ ],
+ get_indicator(doc) {
+ if (doc.status == "Debit Note Issued") {
+ return [__(doc.status), "darkgrey", "status,=," + doc.status];
}
- }
-};
\ No newline at end of file
+
+ if (
+ flt(doc.outstanding_amount) > 0 &&
+ doc.docstatus == 1 &&
+ cint(doc.on_hold)
+ ) {
+ if (!doc.release_date) {
+ return [__("On Hold"), "darkgrey"];
+ } else if (
+ frappe.datetime.get_diff(
+ doc.release_date,
+ frappe.datetime.nowdate()
+ ) > 0
+ ) {
+ return [__("Temporarily on Hold"), "darkgrey"];
+ }
+ }
+
+ const status_colors = {
+ "Unpaid": "orange",
+ "Paid": "green",
+ "Return": "gray",
+ "Overdue": "red",
+ "Partly Paid": "yellow",
+ "Internal Transfer": "darkgrey",
+ };
+
+ if (status_colors[doc.status]) {
+ return [
+ __(doc.status),
+ status_colors[doc.status],
+ "status,=," + doc.status,
+ ];
+ }
+ },
+};
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js
index b470051..94b3b9e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js
@@ -72,4 +72,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 8e6393f..5cbeb56 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -3,20 +3,25 @@
from __future__ import unicode_literals
+
import unittest
-import frappe, erpnext
-import frappe.model
+
+import frappe
+from frappe.utils import add_days, cint, flt, getdate, nowdate, today
+
+import erpnext
+from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-from frappe.utils import cint, flt, today, nowdate, add_days, getdate
-import frappe.defaults
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt, get_taxes
+from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.exceptions import InvalidCurrency
-from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
from erpnext.projects.doctype.project.test_project import make_project
-from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.buying.doctype.supplier.test_supplier import create_supplier
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import (
+ get_taxes,
+ make_purchase_receipt,
+)
+from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
test_ignore = ["Serial No"]
@@ -231,7 +236,9 @@
self.assertEqual(expected_values[gle.account][2], gle.credit)
def test_purchase_invoice_with_exchange_rate_difference(self):
- from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as create_purchase_invoice
+ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+ 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)
@@ -271,7 +278,7 @@
enable_discount_accounting()
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
@@ -401,8 +408,9 @@
self.assertEqual(tax.total, expected_values[i][2])
def test_purchase_invoice_with_advance(self):
- from erpnext.accounts.doctype.journal_entry.test_journal_entry \
- import test_records as jv_test_records
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
+ test_records as jv_test_records,
+ )
jv = frappe.copy_doc(jv_test_records[1])
jv.insert()
@@ -441,8 +449,9 @@
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 test_records as jv_test_records
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
+ test_records as jv_test_records,
+ )
jv = frappe.copy_doc(jv_test_records[1])
jv.insert()
@@ -714,8 +723,9 @@
"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 test_records as jv_test_records
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
+ test_records as jv_test_records,
+ )
jv = frappe.copy_doc(jv_test_records[1])
jv.accounts[0].is_advance = 'Yes'
@@ -794,8 +804,7 @@
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
+ 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")
@@ -1133,19 +1142,20 @@
frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account)
def test_purchase_invoice_advance_taxes(self):
- from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
# create a new supplier to test
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', nowdate())
+ 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')
+ po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000, item='_Test Non Stock Item',
+ posting_date='2021-09-15')
po.apply_tds = 1
po.tax_withholding_category = 'TDS - 194 - Dividends - Individual'
po.save()
@@ -1217,16 +1227,20 @@
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, date):
+def update_tax_witholding_category(company, account):
from erpnext.accounts.utils import get_fiscal_year
- fiscal_year = get_fiscal_year(date=date, company=company)
+ fiscal_year = get_fiscal_year(fiscal_year='2021')
if not frappe.db.get_value('Tax Withholding Rate',
- {'parent': 'TDS - 194 - Dividends - Individual', 'fiscal_year': fiscal_year[0]}):
+ {'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', {
- 'fiscal_year': fiscal_year[0],
+ 'from_date': fiscal_year[1],
+ 'to_date': fiscal_year[2],
'tax_withholding_rate': 10,
'single_threshold': 2500,
'cumulative_threshold': 0
diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
index 63dfff8..9fcbf5c 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
@@ -97,6 +97,7 @@
"width": "100px"
},
{
+ "depends_on": "exchange_gain_loss",
"fieldname": "exchange_gain_loss",
"fieldtype": "Currency",
"label": "Exchange Gain/Loss",
@@ -104,6 +105,7 @@
"read_only": 1
},
{
+ "depends_on": "exchange_gain_loss",
"fieldname": "ref_exchange_rate",
"fieldtype": "Float",
"label": "Reference Exchange Rate",
@@ -115,7 +117,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-04-20 16:26:53.820530",
+ "modified": "2021-09-26 15:47:28.167371",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Advance",
diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py
index bfaa849..ec2ce65 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PurchaseInvoiceAdvance(Document):
- pass
\ No newline at end of file
+ pass
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 51e5c1a..d39a9fc 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -8,6 +8,7 @@
"engine": "InnoDB",
"field_order": [
"item_code",
+ "product_bundle",
"col_break1",
"item_name",
"description_section",
@@ -857,12 +858,19 @@
"fieldtype": "Link",
"label": "Discount Account",
"options": "Account"
+ },
+ {
+ "fieldname": "product_bundle",
+ "fieldtype": "Link",
+ "label": "Product Bundle",
+ "options": "Product Bundle",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-08-12 20:14:45.506639",
+ "modified": "2021-09-01 16:04:03.538643",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
index 50ec7d8..ad2a24c 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PurchaseInvoiceItem(Document):
pass
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py
index a7489da..34ac257 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PurchaseTaxesandCharges(Document):
- pass
\ No newline at end of file
+ pass
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 efcef46..53b549f 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
@@ -3,10 +3,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
-from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template \
- import valdiate_taxes_and_charges_template
+
+from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template import (
+ valdiate_taxes_and_charges_template,
+)
+
class PurchaseTaxesandChargesTemplate(Document):
def validate(self):
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 11c220b..db9793d 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
@@ -19,4 +19,4 @@
'items': ['Supplier Quotation', 'Tax Rule']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js
index c73f03b..10b05d0 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js
@@ -26,4 +26,3 @@
() => done()
]);
});
-
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 97fbca3..c60c81b 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
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Purchase Taxes and Charges Template')
diff --git a/erpnext/accounts/doctype/salary_component_account/salary_component_account.py b/erpnext/accounts/doctype/salary_component_account/salary_component_account.py
index 983d015..d96ef62 100644
--- a/erpnext/accounts/doctype/salary_component_account/salary_component_account.py
+++ b/erpnext/accounts/doctype/salary_component_account/salary_component_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SalaryComponentAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india.js b/erpnext/accounts/doctype/sales_invoice/regional/india.js
index f54bce8..6336db1 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/india.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/india.js
@@ -1,8 +1,6 @@
{% include "erpnext/regional/india/taxes.js" %}
-{% include "erpnext/regional/india/e_invoice/einvoice.js" %}
erpnext.setup_auto_gst_taxation('Sales Invoice');
-erpnext.setup_einvoice_actions('Sales Invoice')
frappe.ui.form.on("Sales Invoice", {
setup: function(frm) {
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
index ada665a..d9d6634 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
@@ -36,139 +36,4 @@
};
list_view.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
-
- const generate_irns = () => {
- const docnames = list_view.get_checked_items(true);
- if (docnames && docnames.length) {
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.generate_einvoices',
- args: { docnames },
- freeze: true,
- freeze_message: __('Generating E-Invoices...')
- });
- } else {
- frappe.msgprint({
- message: __('Please select at least one sales invoice to generate IRN'),
- title: __('No Invoice Selected'),
- indicator: 'red'
- });
- }
- };
-
- const cancel_irns = () => {
- const docnames = list_view.get_checked_items(true);
-
- const fields = [
- {
- "label": "Reason",
- "fieldname": "reason",
- "fieldtype": "Select",
- "reqd": 1,
- "default": "1-Duplicate",
- "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
- },
- {
- "label": "Remark",
- "fieldname": "remark",
- "fieldtype": "Data",
- "reqd": 1
- }
- ];
-
- const d = new frappe.ui.Dialog({
- title: __("Cancel IRN"),
- fields: fields,
- primary_action: function() {
- const data = d.get_values();
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.cancel_irns',
- args: {
- doctype: list_view.doctype,
- docnames,
- reason: data.reason.split('-')[0],
- remark: data.remark
- },
- freeze: true,
- freeze_message: __('Cancelling E-Invoices...'),
- });
- d.hide();
- },
- primary_action_label: __('Submit')
- });
- d.show();
- };
-
- let einvoicing_enabled = false;
- frappe.db.get_single_value("E Invoice Settings", "enable").then(enabled => {
- einvoicing_enabled = enabled;
- });
-
- list_view.$result.on("change", "input[type=checkbox]", () => {
- if (einvoicing_enabled) {
- const docnames = list_view.get_checked_items(true);
- // show/hide e-invoicing actions when no sales invoices are checked
- if (docnames && docnames.length) {
- // prevent adding actions twice if e-invoicing action group already exists
- if (list_view.page.get_inner_group_button(__('E-Invoicing')).length == 0) {
- list_view.page.add_inner_button(__('Generate IRNs'), generate_irns, __('E-Invoicing'));
- list_view.page.add_inner_button(__('Cancel IRNs'), cancel_irns, __('E-Invoicing'));
- }
- } else {
- list_view.page.remove_inner_button(__('Generate IRNs'), __('E-Invoicing'));
- list_view.page.remove_inner_button(__('Cancel IRNs'), __('E-Invoicing'));
- }
- }
- });
-
- frappe.realtime.on("bulk_einvoice_generation_complete", (data) => {
- const { failures, user, invoices } = data;
-
- if (invoices.length != failures.length) {
- frappe.msgprint({
- message: __('{0} e-invoices generated successfully', [invoices.length]),
- title: __('Bulk E-Invoice Generation Complete'),
- indicator: 'orange'
- });
- }
-
- if (failures && failures.length && user == frappe.session.user) {
- let message = `
- Failed to generate IRNs for following ${failures.length} sales invoices:
- <ul style="padding-left: 20px; padding-top: 5px;">
- ${failures.map(d => `<li>${d.docname}</li>`).join('')}
- </ul>
- `;
- frappe.msgprint({
- message: message,
- title: __('Bulk E-Invoice Generation Complete'),
- indicator: 'orange'
- });
- }
- });
-
- frappe.realtime.on("bulk_einvoice_cancellation_complete", (data) => {
- const { failures, user, invoices } = data;
-
- if (invoices.length != failures.length) {
- frappe.msgprint({
- message: __('{0} e-invoices cancelled successfully', [invoices.length]),
- title: __('Bulk E-Invoice Cancellation Complete'),
- indicator: 'orange'
- });
- }
-
- if (failures && failures.length && user == frappe.session.user) {
- let message = `
- Failed to cancel IRNs for following ${failures.length} sales invoices:
- <ul style="padding-left: 20px; padding-top: 5px;">
- ${failures.map(d => `<li>${d.docname}</li>`).join('')}
- </ul>
- `;
- frappe.msgprint({
- message: message,
- title: __('Bulk E-Invoice Cancellation Complete'),
- indicator: 'orange'
- });
- }
- });
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/italy.js b/erpnext/accounts/doctype/sales_invoice/regional/italy.js
index 1c47d3a..21eb8ce 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/italy.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/italy.js
@@ -1,3 +1,3 @@
{% include "erpnext/regional/italy/sales_invoice.js" %}
-erpnext.setup_e_invoice_button('Sales Invoice')
\ No newline at end of file
+erpnext.setup_e_invoice_button('Sales Invoice')
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 2751b55..73e1284 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -154,9 +154,9 @@
return
}
- $.each(doc["items"], function(i, row) {
+ doc.items.forEach((row) => {
if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note)
- })
+ });
}
set_default_print_format() {
@@ -324,16 +324,12 @@
}
write_off_outstanding_amount_automatically() {
- if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
+ if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
// this will make outstanding amount 0
this.frm.set_value("write_off_amount",
flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount"))
);
- this.frm.toggle_enable("write_off_amount", false);
-
- } else {
- this.frm.toggle_enable("write_off_amount", true);
}
this.calculate_outstanding_amount(false);
@@ -448,6 +444,18 @@
this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount");
}
+
+ currency() {
+ var me = this;
+ super.currency();
+ if (this.frm.doc.timesheets) {
+ this.frm.doc.timesheets.forEach((d) => {
+ let row = frappe.get_doc(d.doctype, d.name)
+ set_timesheet_detail_rate(row.doctype, row.name, me.frm.doc.currency, row.timesheet_detail)
+ });
+ frm.trigger("calculate_timesheet_totals");
+ }
+ }
};
// for backward compatibility: combine new and previous states
@@ -561,6 +569,9 @@
frm.add_fetch('payment_term', 'invoice_portion', 'invoice_portion');
frm.add_fetch('payment_term', 'description', 'description');
+ frm.set_df_property('packed_items', 'cannot_add_rows', true);
+ frm.set_df_property('packed_items', 'cannot_delete_rows', true);
+
frm.set_query("account_for_change_amount", function() {
return {
filters: {
@@ -714,19 +725,6 @@
}
},
- project: function(frm){
- if (!frm.doc.is_return) {
- frm.call({
- method: "add_timesheet_data",
- doc: frm.doc,
- callback: function(r, rt) {
- refresh_field(['timesheets'])
- }
- })
- frm.refresh();
- }
- },
-
onload: function(frm) {
frm.redemption_conversion_factor = null;
},
@@ -778,8 +776,6 @@
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
else hide_field(['c_form_applicable', 'c_form_no']);
- frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
-
frm.refresh_fields();
},
@@ -839,24 +835,92 @@
}
},
- add_timesheet_row: function(frm, row, exchange_rate) {
- frm.add_child('timesheets', {
- 'activity_type': row.activity_type,
- 'description': row.description,
- 'time_sheet': row.parent,
- 'billing_hours': row.billing_hours,
- 'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
- 'timesheet_detail': row.name
+ project: function(frm) {
+ if (frm.doc.project) {
+ frm.events.add_timesheet_data(frm, {
+ project: frm.doc.project
+ });
+ }
+ },
+
+ async add_timesheet_data(frm, kwargs) {
+ if (kwargs === "Sales Invoice") {
+ // called via frm.trigger()
+ kwargs = Object();
+ }
+
+ if (!kwargs.hasOwnProperty("project") && frm.doc.project) {
+ kwargs.project = frm.doc.project;
+ }
+
+ const timesheets = await frm.events.get_timesheet_data(frm, kwargs);
+ return frm.events.set_timesheet_data(frm, timesheets);
+ },
+
+ async get_timesheet_data(frm, kwargs) {
+ return frappe.call({
+ method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
+ args: kwargs
+ }).then(r => {
+ if (!r.exc && r.message.length > 0) {
+ return r.message
+ } else {
+ return []
+ }
});
- frm.refresh_field('timesheets');
- calculate_total_billing_amount(frm);
+ },
+
+ set_timesheet_data: function(frm, timesheets) {
+ frm.clear_table("timesheets")
+ timesheets.forEach(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);
+ }
+ }
+ });
+ } else {
+ frm.events.append_time_log(frm, timesheet, 1.0);
+ }
+ });
+ },
+
+ append_time_log: function(frm, time_log, exchange_rate) {
+ const row = frm.add_child("timesheets");
+ row.activity_type = time_log.activity_type;
+ row.description = time_log.description;
+ row.time_sheet = time_log.time_sheet;
+ row.from_time = time_log.from_time;
+ row.to_time = time_log.to_time;
+ 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;
+
+ frm.refresh_field("timesheets");
+ frm.trigger("calculate_timesheet_totals");
+ },
+
+ calculate_timesheet_totals: function(frm) {
+ frm.set_value("total_billing_amount",
+ frm.doc.timesheets.reduce((a, b) => a + (b["billing_amount"] || 0.0), 0.0));
+ frm.set_value("total_billing_hours",
+ frm.doc.timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0));
},
refresh: function(frm) {
if (frm.doc.docstatus===0 && !frm.doc.is_return) {
- frm.add_custom_button(__('Fetch Timesheet'), function() {
+ frm.add_custom_button(__("Fetch Timesheet"), function() {
let d = new frappe.ui.Dialog({
- title: __('Fetch Timesheet'),
+ title: __("Fetch Timesheet"),
fields: [
{
"label" : __("From"),
@@ -865,8 +929,8 @@
"reqd": 1,
},
{
- fieldtype: 'Column Break',
- fieldname: 'col_break_1',
+ fieldtype: "Column Break",
+ fieldname: "col_break_1",
},
{
"label" : __("To"),
@@ -883,48 +947,18 @@
},
],
primary_action: function() {
- let data = d.get_values();
- frappe.call({
- method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
- args: {
- from_time: data.from_time,
- to_time: data.to_time,
- project: data.project
- },
- callback: function(r) {
- if (!r.exc && r.message.length > 0) {
- frm.clear_table('timesheets')
- r.message.forEach((d) => {
- let exchange_rate = 1.0;
- if (frm.doc.currency != d.currency) {
- frappe.call({
- method: 'erpnext.setup.utils.get_exchange_rate',
- args: {
- from_currency: d.currency,
- to_currency: frm.doc.currency
- },
- callback: function(r) {
- if (r.message) {
- exchange_rate = r.message;
- frm.events.add_timesheet_row(frm, d, exchange_rate);
- }
- }
- });
- } else {
- frm.events.add_timesheet_row(frm, d, exchange_rate);
- }
- });
- } else {
- frappe.msgprint(__('No Timesheets found with the selected filters.'))
- }
- d.hide();
- }
+ const data = d.get_values();
+ frm.events.add_timesheet_data(frm, {
+ from_time: data.from_time,
+ to_time: data.to_time,
+ project: data.project
});
+ d.hide();
},
- primary_action_label: __('Get Timesheets')
+ primary_action_label: __("Get Timesheets")
});
d.show();
- })
+ });
}
if (frm.doc.is_debit_note) {
@@ -957,49 +991,34 @@
frm: frm
});
},
+
create_dunning: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
frm: frm
});
}
-})
+});
-frappe.ui.form.on('Sales Invoice Timesheet', {
- time_sheet: function(frm, cdt, cdn){
- var d = locals[cdt][cdn];
- if(d.time_sheet) {
- frappe.call({
- method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data",
- args: {
- 'name': d.time_sheet,
- 'project': frm.doc.project || null
- },
- callback: function(r, rt) {
- if(r.message){
- let data = r.message;
- frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
- frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
- frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
- calculate_total_billing_amount(frm)
- }
- }
- })
+frappe.ui.form.on("Sales Invoice Timesheet", {
+ timesheets_remove(frm) {
+ frm.trigger("calculate_timesheet_totals");
+ }
+});
+
+var set_timesheet_detail_rate = function(cdt, cdn, currency, timelog) {
+ frappe.call({
+ method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_detail_rate",
+ args: {
+ timelog: timelog,
+ currency: currency
+ },
+ callback: function(r) {
+ if (!r.exc && r.message) {
+ frappe.model.set_value(cdt, cdn, 'billing_amount', r.message);
+ }
}
- }
-})
-
-var calculate_total_billing_amount = function(frm) {
- var doc = frm.doc;
-
- doc.total_billing_amount = 0.0
- if(doc.timesheets) {
- $.each(doc.timesheets, function(index, data){
- doc.total_billing_amount += data.billing_amount
- })
- }
-
- refresh_field('total_billing_amount')
+ });
}
var select_loyalty_program = function(frm, loyalty_programs) {
@@ -1032,276 +1051,3 @@
dialog.show();
}
-
-// Healthcare
-var get_healthcare_services_to_invoice = function(frm) {
- var me = this;
- let selected_patient = '';
- var dialog = new frappe.ui.Dialog({
- title: __("Get Items from Healthcare Services"),
- fields:[
- {
- fieldtype: 'Link',
- options: 'Patient',
- label: 'Patient',
- fieldname: "patient",
- reqd: true
- },
- { fieldtype: 'Section Break' },
- { fieldtype: 'HTML', fieldname: 'results_area' }
- ]
- });
- var $wrapper;
- var $results;
- var $placeholder;
- dialog.set_values({
- 'patient': frm.doc.patient
- });
- dialog.fields_dict["patient"].df.onchange = () => {
- var patient = dialog.fields_dict.patient.input.value;
- if(patient && patient!=selected_patient){
- selected_patient = patient;
- var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice";
- var args = {patient: patient, company: frm.doc.company};
- var columns = (["service", "reference_name", "reference_type"]);
- get_healthcare_items(frm, true, $results, $placeholder, method, args, columns);
- }
- else if(!patient){
- selected_patient = '';
- $results.empty();
- $results.append($placeholder);
- }
- }
- $wrapper = dialog.fields_dict.results_area.$wrapper.append(`<div class="results"
- style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
- $results = $wrapper.find('.results');
- $placeholder = $(`<div class="multiselect-empty-state">
- <span class="text-center" style="margin-top: -40px;">
- <i class="fa fa-2x fa-heartbeat text-extra-muted"></i>
- <p class="text-extra-muted">No billable Healthcare Services found</p>
- </span>
- </div>`);
- $results.on('click', '.list-item--head :checkbox', (e) => {
- $results.find('.list-item-container .list-row-check')
- .prop("checked", ($(e.target).is(':checked')));
- });
- set_primary_action(frm, dialog, $results, true);
- dialog.show();
-};
-
-var get_healthcare_items = function(frm, invoice_healthcare_services, $results, $placeholder, method, args, columns) {
- var me = this;
- $results.empty();
- frappe.call({
- method: method,
- args: args,
- callback: function(data) {
- if(data.message){
- $results.append(make_list_row(columns, invoice_healthcare_services));
- for(let i=0; i<data.message.length; i++){
- $results.append(make_list_row(columns, invoice_healthcare_services, data.message[i]));
- }
- }else {
- $results.append($placeholder);
- }
- }
- });
-}
-
-var make_list_row= function(columns, invoice_healthcare_services, result={}) {
- var me = this;
- // Make a head row by default (if result not passed)
- let head = Object.keys(result).length === 0;
- let contents = ``;
- columns.forEach(function(column) {
- contents += `<div class="list-item__content ellipsis">
- ${
- head ? `<span class="ellipsis">${__(frappe.model.unscrub(column))}</span>`
-
- :(column !== "name" ? `<span class="ellipsis">${__(result[column])}</span>`
- : `<a class="list-id ellipsis">
- ${__(result[column])}</a>`)
- }
- </div>`;
- })
-
- let $row = $(`<div class="list-item">
- <div class="list-item__content" style="flex: 0 0 10px;">
- <input type="checkbox" class="list-row-check" ${result.checked ? 'checked' : ''}>
- </div>
- ${contents}
- </div>`);
-
- $row = list_row_data_items(head, $row, result, invoice_healthcare_services);
- return $row;
-};
-
-var set_primary_action= function(frm, dialog, $results, invoice_healthcare_services) {
- var me = this;
- dialog.set_primary_action(__('Add'), function() {
- let checked_values = get_checked_values($results);
- if(checked_values.length > 0){
- if(invoice_healthcare_services) {
- frm.set_value("patient", dialog.fields_dict.patient.input.value);
- }
- frm.set_value("items", []);
- add_to_item_line(frm, checked_values, invoice_healthcare_services);
- dialog.hide();
- }
- else{
- if(invoice_healthcare_services){
- frappe.msgprint(__("Please select Healthcare Service"));
- }
- else{
- frappe.msgprint(__("Please select Drug"));
- }
- }
- });
-};
-
-var get_checked_values= function($results) {
- return $results.find('.list-item-container').map(function() {
- let checked_values = {};
- if ($(this).find('.list-row-check:checkbox:checked').length > 0 ) {
- checked_values['dn'] = $(this).attr('data-dn');
- checked_values['dt'] = $(this).attr('data-dt');
- checked_values['item'] = $(this).attr('data-item');
- if($(this).attr('data-rate') != 'undefined'){
- checked_values['rate'] = $(this).attr('data-rate');
- }
- else{
- checked_values['rate'] = false;
- }
- if($(this).attr('data-income-account') != 'undefined'){
- checked_values['income_account'] = $(this).attr('data-income-account');
- }
- else{
- checked_values['income_account'] = false;
- }
- if($(this).attr('data-qty') != 'undefined'){
- checked_values['qty'] = $(this).attr('data-qty');
- }
- else{
- checked_values['qty'] = false;
- }
- if($(this).attr('data-description') != 'undefined'){
- checked_values['description'] = $(this).attr('data-description');
- }
- else{
- checked_values['description'] = false;
- }
- return checked_values;
- }
- }).get();
-};
-
-var get_drugs_to_invoice = function(frm) {
- var me = this;
- let selected_encounter = '';
- var dialog = new frappe.ui.Dialog({
- title: __("Get Items from Prescriptions"),
- fields:[
- { fieldtype: 'Link', options: 'Patient', label: 'Patient', fieldname: "patient", reqd: true },
- { fieldtype: 'Link', options: 'Patient Encounter', label: 'Patient Encounter', fieldname: "encounter", reqd: true,
- description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.',
- get_query: function(doc) {
- return {
- filters: {
- patient: dialog.get_value("patient"),
- company: frm.doc.company,
- docstatus: 1
- }
- };
- }
- },
- { fieldtype: 'Section Break' },
- { fieldtype: 'HTML', fieldname: 'results_area' }
- ]
- });
- var $wrapper;
- var $results;
- var $placeholder;
- dialog.set_values({
- 'patient': frm.doc.patient,
- 'encounter': ""
- });
- dialog.fields_dict["encounter"].df.onchange = () => {
- var encounter = dialog.fields_dict.encounter.input.value;
- if(encounter && encounter!=selected_encounter){
- selected_encounter = encounter;
- var method = "erpnext.healthcare.utils.get_drugs_to_invoice";
- var args = {encounter: encounter};
- var columns = (["drug_code", "quantity", "description"]);
- get_healthcare_items(frm, false, $results, $placeholder, method, args, columns);
- }
- else if(!encounter){
- selected_encounter = '';
- $results.empty();
- $results.append($placeholder);
- }
- }
- $wrapper = dialog.fields_dict.results_area.$wrapper.append(`<div class="results"
- style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
- $results = $wrapper.find('.results');
- $placeholder = $(`<div class="multiselect-empty-state">
- <span class="text-center" style="margin-top: -40px;">
- <i class="fa fa-2x fa-heartbeat text-extra-muted"></i>
- <p class="text-extra-muted">No Drug Prescription found</p>
- </span>
- </div>`);
- $results.on('click', '.list-item--head :checkbox', (e) => {
- $results.find('.list-item-container .list-row-check')
- .prop("checked", ($(e.target).is(':checked')));
- });
- set_primary_action(frm, dialog, $results, false);
- dialog.show();
-};
-
-var list_row_data_items = function(head, $row, result, invoice_healthcare_services) {
- if(invoice_healthcare_services){
- head ? $row.addClass('list-item--head')
- : $row = $(`<div class="list-item-container"
- data-dn= "${result.reference_name}" data-dt= "${result.reference_type}" data-item= "${result.service}"
- data-rate = ${result.rate}
- data-income-account = "${result.income_account}"
- data-qty = ${result.qty}
- data-description = "${result.description}">
- </div>`).append($row);
- }
- else{
- head ? $row.addClass('list-item--head')
- : $row = $(`<div class="list-item-container"
- data-item= "${result.drug_code}"
- data-qty = ${result.quantity}
- data-description = "${result.description}">
- </div>`).append($row);
- }
- return $row
-};
-
-var add_to_item_line = function(frm, checked_values, invoice_healthcare_services){
- if(invoice_healthcare_services){
- frappe.call({
- doc: frm.doc,
- method: "set_healthcare_services",
- args:{
- checked_values: checked_values
- },
- callback: function() {
- frm.trigger("validate");
- frm.refresh_fields();
- }
- });
- }
- else{
- for(let i=0; i<checked_values.length; i++){
- var si_item = frappe.model.add_child(frm.doc, 'Sales Invoice Item', 'items');
- frappe.model.set_value(si_item.doctype, si_item.name, 'item_code', checked_values[i]['item']);
- frappe.model.set_value(si_item.doctype, si_item.name, 'qty', 1);
- if(checked_values[i]['qty'] > 1){
- frappe.model.set_value(si_item.doctype, si_item.name, 'qty', parseFloat(checked_values[i]['qty']));
- }
- }
- frm.refresh_fields();
- }
-};
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 9ab3547..f3adb89 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -74,6 +74,7 @@
"time_sheet_list",
"timesheets",
"total_billing_amount",
+ "total_billing_hours",
"section_break_30",
"total_qty",
"base_total",
@@ -199,9 +200,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "options": "fa fa-user",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-user"
},
{
"allow_on_submit": 1,
@@ -213,9 +212,7 @@
"hide_seconds": 1,
"label": "Title",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"bold": 1,
@@ -230,9 +227,7 @@
"options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-",
"print_hide": 1,
"reqd": 1,
- "set_only_once": 1,
- "show_days": 1,
- "show_seconds": 1
+ "set_only_once": 1
},
{
"bold": 1,
@@ -246,25 +241,21 @@
"oldfieldtype": "Link",
"options": "Customer",
"print_hide": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"bold": 1,
"depends_on": "customer",
"fetch_from": "customer.customer_name",
"fieldname": "customer_name",
- "fieldtype": "Data",
+ "fieldtype": "Small Text",
"hide_days": 1,
"hide_seconds": 1,
"in_global_search": 1,
"label": "Customer Name",
"oldfieldname": "customer_name",
"oldfieldtype": "Data",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "tax_id",
@@ -273,9 +264,7 @@
"hide_seconds": 1,
"label": "Tax Id",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "project",
@@ -287,9 +276,7 @@
"oldfieldname": "project_name",
"oldfieldtype": "Link",
"options": "Project",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -300,9 +287,7 @@
"label": "Include Payment (POS)",
"oldfieldname": "is_pos",
"oldfieldtype": "Check",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "is_pos",
@@ -312,9 +297,7 @@
"hide_seconds": 1,
"label": "POS Profile",
"options": "POS Profile",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -324,18 +307,14 @@
"hide_seconds": 1,
"label": "Is Return (Credit Note)",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
- "oldfieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Column Break"
},
{
"fieldname": "company",
@@ -349,9 +328,7 @@
"options": "Company",
"print_hide": 1,
"remember_last_selected_value": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "cost_center",
@@ -359,9 +336,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Cost Center",
- "options": "Cost Center",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Cost Center"
},
{
"bold": 1,
@@ -375,9 +350,7 @@
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"fieldname": "posting_time",
@@ -388,9 +361,7 @@
"no_copy": 1,
"oldfieldname": "posting_time",
"oldfieldtype": "Time",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -400,9 +371,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Edit Posting Date and Time",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "due_date",
@@ -412,9 +381,7 @@
"label": "Payment Due Date",
"no_copy": 1,
"oldfieldname": "due_date",
- "oldfieldtype": "Date",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Date"
},
{
"fieldname": "amended_from",
@@ -428,9 +395,7 @@
"oldfieldtype": "Link",
"options": "Sales Invoice",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:doc.return_against || doc.is_debit_note",
@@ -443,9 +408,7 @@
"options": "Sales Invoice",
"print_hide": 1,
"read_only_depends_on": "eval:doc.is_return",
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"default": "0",
@@ -454,9 +417,7 @@
"fieldtype": "Check",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Update Billed Amount in Sales Order",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Update Billed Amount in Sales Order"
},
{
"collapsible": 1,
@@ -465,9 +426,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Customer PO Details",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Customer PO Details"
},
{
"allow_on_submit": 1,
@@ -477,17 +436,13 @@
"hide_seconds": 1,
"label": "Customer's Purchase Order",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_23",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"allow_on_submit": 1,
@@ -495,9 +450,7 @@
"fieldtype": "Date",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Customer's Purchase Order Date",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Customer's Purchase Order Date"
},
{
"collapsible": 1,
@@ -505,9 +458,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Address and Contact",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Address and Contact"
},
{
"fieldname": "customer_address",
@@ -516,9 +467,7 @@
"hide_seconds": 1,
"label": "Customer Address",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "address_display",
@@ -526,9 +475,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Address",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "contact_person",
@@ -538,9 +485,7 @@
"in_global_search": 1,
"label": "Contact Person",
"options": "Contact",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "contact_display",
@@ -548,9 +493,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Contact",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "contact_mobile",
@@ -559,9 +502,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Mobile No",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "contact_email",
@@ -572,9 +513,7 @@
"label": "Contact Email",
"options": "Email",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "territory",
@@ -583,17 +522,13 @@
"hide_seconds": 1,
"label": "Territory",
"options": "Territory",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "col_break4",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "shipping_address_name",
@@ -602,9 +537,7 @@
"hide_seconds": 1,
"label": "Shipping Address Name",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "shipping_address",
@@ -613,9 +546,7 @@
"hide_seconds": 1,
"label": "Shipping Address",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "company_address",
@@ -624,9 +555,7 @@
"hide_seconds": 1,
"label": "Company Address Name",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "company_address_display",
@@ -636,9 +565,7 @@
"hide_seconds": 1,
"label": "Company Address",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
@@ -647,9 +574,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Currency and Price List",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Currency and Price List"
},
{
"fieldname": "currency",
@@ -661,9 +586,7 @@
"oldfieldtype": "Select",
"options": "Currency",
"print_hide": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"description": "Rate at which Customer Currency is converted to customer's base currency",
@@ -676,17 +599,13 @@
"oldfieldtype": "Currency",
"precision": "9",
"print_hide": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -699,9 +618,7 @@
"oldfieldtype": "Select",
"options": "Price List",
"print_hide": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "price_list_currency",
@@ -712,9 +629,7 @@
"options": "Currency",
"print_hide": 1,
"read_only": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"description": "Rate at which Price list currency is converted to customer's base currency",
@@ -725,9 +640,7 @@
"label": "Price List Exchange Rate",
"precision": "9",
"print_hide": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"default": "0",
@@ -738,18 +651,14 @@
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "sec_warehouse",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Warehouse",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Warehouse"
},
{
"depends_on": "update_stock",
@@ -759,9 +668,7 @@
"hide_seconds": 1,
"label": "Source Warehouse",
"options": "Warehouse",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "items_section",
@@ -770,9 +677,7 @@
"hide_seconds": 1,
"label": "Items",
"oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-shopping-cart"
},
{
"default": "0",
@@ -783,9 +688,7 @@
"label": "Update Stock",
"oldfieldname": "update_stock",
"oldfieldtype": "Check",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "scan_barcode",
@@ -793,8 +696,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Scan Barcode",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -806,18 +708,14 @@
"oldfieldname": "entries",
"oldfieldtype": "Table",
"options": "Sales Invoice Item",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "pricing_rule_details",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Pricing Rules",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Pricing Rules"
},
{
"fieldname": "pricing_rules",
@@ -826,31 +724,27 @@
"hide_seconds": 1,
"label": "Pricing Rule Detail",
"options": "Pricing Rule Detail",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Packing List",
"options": "fa fa-suitcase",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"hide_days": 1,
"hide_seconds": 1,
"label": "Packed Items",
"options": "Packed Item",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "product_bundle_help",
@@ -858,9 +752,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Product Bundle Help",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -870,9 +762,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Time Sheet List",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Time Sheet List"
},
{
"fieldname": "timesheets",
@@ -881,9 +771,7 @@
"hide_seconds": 1,
"label": "Time Sheets",
"options": "Sales Invoice Timesheet",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -894,17 +782,13 @@
"label": "Total Billing Amount",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "section_break_30",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "total_qty",
@@ -912,9 +796,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Total Quantity",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_total",
@@ -924,9 +806,7 @@
"label": "Total (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_net_total",
@@ -939,17 +819,13 @@
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "column_break_32",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "total",
@@ -958,9 +834,7 @@
"hide_seconds": 1,
"label": "Total",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "net_total",
@@ -970,9 +844,7 @@
"label": "Net Total",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "total_net_weight",
@@ -981,9 +853,7 @@
"hide_seconds": 1,
"label": "Total Net Weight",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "taxes_section",
@@ -991,9 +861,7 @@
"hide_days": 1,
"hide_seconds": 1,
"oldfieldtype": "Section Break",
- "options": "fa fa-money",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-money"
},
{
"fieldname": "taxes_and_charges",
@@ -1004,17 +872,13 @@
"oldfieldname": "charge",
"oldfieldtype": "Link",
"options": "Sales Taxes and Charges Template",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_38",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "shipping_rule",
@@ -1024,9 +888,7 @@
"label": "Shipping Rule",
"oldfieldtype": "Button",
"options": "Shipping Rule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "tax_category",
@@ -1035,17 +897,13 @@
"hide_seconds": 1,
"label": "Tax Category",
"options": "Tax Category",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "section_break_40",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "taxes",
@@ -1055,9 +913,7 @@
"label": "Sales Taxes and Charges",
"oldfieldname": "other_charges",
"oldfieldtype": "Table",
- "options": "Sales Taxes and Charges",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Sales Taxes and Charges"
},
{
"collapsible": 1,
@@ -1065,9 +921,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Tax Breakup",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Tax Breakup"
},
{
"fieldname": "other_charges_calculation",
@@ -1078,17 +932,13 @@
"no_copy": 1,
"oldfieldtype": "HTML",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "section_break_43",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "base_total_taxes_and_charges",
@@ -1100,17 +950,13 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_47",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "total_taxes_and_charges",
@@ -1120,9 +966,7 @@
"label": "Total Taxes and Charges",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
@@ -1130,9 +974,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Loyalty Points Redemption",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Loyalty Points Redemption"
},
{
"depends_on": "redeem_loyalty_points",
@@ -1142,9 +984,7 @@
"hide_seconds": 1,
"label": "Loyalty Points",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "redeem_loyalty_points",
@@ -1156,9 +996,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "0",
@@ -1168,17 +1006,13 @@
"hide_seconds": 1,
"label": "Redeem Loyalty Points",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_77",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fetch_from": "customer.loyalty_program",
@@ -1190,9 +1024,7 @@
"no_copy": 1,
"options": "Loyalty Program",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "redeem_loyalty_points",
@@ -1202,9 +1034,7 @@
"hide_seconds": 1,
"label": "Redemption Account",
"no_copy": 1,
- "options": "Account",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Account"
},
{
"depends_on": "redeem_loyalty_points",
@@ -1214,9 +1044,7 @@
"hide_seconds": 1,
"label": "Redemption Cost Center",
"no_copy": 1,
- "options": "Cost Center",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Cost Center"
},
{
"collapsible": 1,
@@ -1225,9 +1053,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Additional Discount",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Additional Discount"
},
{
"default": "Grand Total",
@@ -1236,10 +1062,9 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Apply Additional Discount On",
+ "length": 15,
"options": "\nGrand Total\nNet Total",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "base_discount_amount",
@@ -1249,17 +1074,13 @@
"label": "Additional Discount Amount (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_51",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "additional_discount_percentage",
@@ -1267,9 +1088,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Additional Discount Percentage",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "discount_amount",
@@ -1278,9 +1097,7 @@
"hide_seconds": 1,
"label": "Additional Discount Amount",
"options": "currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "totals",
@@ -1289,9 +1106,7 @@
"hide_seconds": 1,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "base_grand_total",
@@ -1304,9 +1119,7 @@
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1318,9 +1131,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1333,14 +1144,12 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"description": "In Words will be visible once you save the Sales Invoice.",
"fieldname": "base_in_words",
- "fieldtype": "Data",
+ "fieldtype": "Small Text",
"hide_days": 1,
"hide_seconds": 1,
"label": "In Words (Company Currency)",
@@ -1348,9 +1157,7 @@
"oldfieldname": "in_words",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break5",
@@ -1359,8 +1166,6 @@
"hide_seconds": 1,
"oldfieldtype": "Column Break",
"print_hide": 1,
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -1375,9 +1180,7 @@
"oldfieldtype": "Currency",
"options": "currency",
"read_only": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -1389,9 +1192,7 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"bold": 1,
@@ -1404,13 +1205,11 @@
"oldfieldname": "rounded_total_export",
"oldfieldtype": "Currency",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "in_words",
- "fieldtype": "Data",
+ "fieldtype": "Small Text",
"hide_days": 1,
"hide_seconds": 1,
"label": "In Words",
@@ -1418,9 +1217,7 @@
"oldfieldname": "in_words_export",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "total_advance",
@@ -1432,9 +1229,7 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "outstanding_amount",
@@ -1447,9 +1242,7 @@
"oldfieldtype": "Currency",
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
@@ -1461,9 +1254,7 @@
"label": "Advance Payments",
"oldfieldtype": "Section Break",
"options": "fa fa-money",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -1471,9 +1262,7 @@
"fieldtype": "Check",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Allocate Advances Automatically (FIFO)",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Allocate Advances Automatically (FIFO)"
},
{
"depends_on": "eval:!doc.allocate_advances_automatically",
@@ -1482,9 +1271,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Get Advances Received",
- "options": "set_advances",
- "show_days": 1,
- "show_seconds": 1
+ "options": "set_advances"
},
{
"fieldname": "advances",
@@ -1495,9 +1282,7 @@
"oldfieldname": "advance_adjustment_details",
"oldfieldtype": "Table",
"options": "Sales Invoice Advance",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1506,9 +1291,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Payment Terms",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Payment Terms"
},
{
"depends_on": "eval:(!doc.is_pos && !doc.is_return)",
@@ -1519,9 +1302,7 @@
"label": "Payment Terms Template",
"no_copy": 1,
"options": "Payment Terms Template",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "eval:(!doc.is_pos && !doc.is_return)",
@@ -1532,9 +1313,7 @@
"label": "Payment Schedule",
"no_copy": 1,
"options": "Payment Schedule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)",
@@ -1543,9 +1322,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Payments",
- "options": "fa fa-money",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-money"
},
{
"depends_on": "is_pos",
@@ -1558,9 +1335,7 @@
"oldfieldname": "cash_bank_account",
"oldfieldtype": "Link",
"options": "Account",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "eval:doc.is_pos===1",
@@ -1570,17 +1345,13 @@
"hide_seconds": 1,
"label": "Sales Invoice Payment",
"options": "Sales Invoice Payment",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "section_break_84",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "base_paid_amount",
@@ -1591,17 +1362,13 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_86",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points",
@@ -1615,17 +1382,13 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "section_break_88",
"fieldtype": "Section Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"depends_on": "is_pos",
@@ -1637,17 +1400,13 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_90",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"depends_on": "is_pos",
@@ -1658,9 +1417,7 @@
"label": "Change Amount",
"no_copy": 1,
"options": "currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "is_pos",
@@ -1670,9 +1427,7 @@
"hide_seconds": 1,
"label": "Account for Change Amount",
"options": "Account",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1683,8 +1438,6 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Write Off",
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -1696,8 +1449,7 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only_depends_on": "eval:doc.write_off_outstanding_amount_automatically"
},
{
"fieldname": "base_write_off_amount",
@@ -1708,9 +1460,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "0",
@@ -1720,17 +1470,13 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Write Off Outstanding Amount",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_74",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"fieldname": "write_off_account",
@@ -1739,9 +1485,7 @@
"hide_seconds": 1,
"label": "Write Off Account",
"options": "Account",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "write_off_cost_center",
@@ -1750,9 +1494,7 @@
"hide_seconds": 1,
"label": "Write Off Cost Center",
"options": "Cost Center",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1762,9 +1504,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Terms and Conditions",
- "oldfieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Section Break"
},
{
"fieldname": "tc_name",
@@ -1775,9 +1515,7 @@
"oldfieldname": "tc_name",
"oldfieldtype": "Link",
"options": "Terms and Conditions",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "terms",
@@ -1786,9 +1524,7 @@
"hide_seconds": 1,
"label": "Terms and Conditions Details",
"oldfieldname": "terms",
- "oldfieldtype": "Text Editor",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Text Editor"
},
{
"collapsible": 1,
@@ -1796,9 +1532,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Printing Settings",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Printing Settings"
},
{
"allow_on_submit": 1,
@@ -1810,9 +1544,7 @@
"oldfieldname": "letter_head",
"oldfieldtype": "Select",
"options": "Letter Head",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1822,9 +1554,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Group same items",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "language",
@@ -1832,18 +1562,15 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Print Language",
+ "length": 6,
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_84",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"allow_on_submit": 1,
@@ -1857,9 +1584,7 @@
"oldfieldtype": "Link",
"options": "Print Heading",
"print_hide": 1,
- "report_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "report_hide": 1
},
{
"collapsible": 1,
@@ -1868,9 +1593,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "More Information",
- "show_days": 1,
- "show_seconds": 1
+ "label": "More Information"
},
{
"fieldname": "inter_company_invoice_reference",
@@ -1879,9 +1602,7 @@
"hide_seconds": 1,
"label": "Inter Company Invoice Reference",
"options": "Purchase Invoice",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "customer_group",
@@ -1891,9 +1612,7 @@
"hide_seconds": 1,
"label": "Customer Group",
"options": "Customer Group",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "campaign",
@@ -1904,9 +1623,7 @@
"oldfieldname": "campaign",
"oldfieldtype": "Link",
"options": "Campaign",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -1916,17 +1633,13 @@
"hide_seconds": 1,
"label": "Is Discounted",
"no_copy": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "col_break23",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -1937,12 +1650,11 @@
"hide_seconds": 1,
"in_standard_filter": 1,
"label": "Status",
+ "length": 30,
"no_copy": 1,
- "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
+ "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nUnpaid and Discounted\nPartly Paid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "source",
@@ -1953,9 +1665,7 @@
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "Lead Source",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1966,9 +1676,7 @@
"label": "Accounting Details",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "debit_to",
@@ -1981,9 +1689,7 @@
"options": "Account",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"fieldname": "party_account_currency",
@@ -1995,9 +1701,7 @@
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "No",
@@ -2006,12 +1710,11 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Is Opening Entry",
+ "length": 4,
"oldfieldname": "is_opening",
"oldfieldtype": "Select",
"options": "No\nYes",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "c_form_applicable",
@@ -2019,11 +1722,10 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "C-Form Applicable",
+ "length": 4,
"no_copy": 1,
"options": "No\nYes",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "c_form_no",
@@ -2034,9 +1736,7 @@
"no_copy": 1,
"options": "C-Form",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break8",
@@ -2044,9 +1744,7 @@
"hide_days": 1,
"hide_seconds": 1,
"oldfieldtype": "Column Break",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "remarks",
@@ -2057,9 +1755,7 @@
"no_copy": 1,
"oldfieldname": "remarks",
"oldfieldtype": "Text",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -2071,9 +1767,7 @@
"label": "Commission",
"oldfieldtype": "Section Break",
"options": "fa fa-group",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "sales_partner",
@@ -2084,9 +1778,7 @@
"oldfieldname": "sales_partner",
"oldfieldtype": "Link",
"options": "Sales Partner",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break10",
@@ -2095,8 +1787,6 @@
"hide_seconds": 1,
"oldfieldtype": "Column Break",
"print_hide": 1,
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -2107,9 +1797,7 @@
"label": "Commission Rate (%)",
"oldfieldname": "commission_rate",
"oldfieldtype": "Currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "total_commission",
@@ -2120,9 +1808,7 @@
"oldfieldname": "total_commission",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -2132,9 +1818,7 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Sales Team",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -2146,9 +1830,7 @@
"oldfieldname": "sales_team",
"oldfieldtype": "Table",
"options": "Sales Team",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -2156,9 +1838,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Subscription Section",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Subscription Section"
},
{
"allow_on_submit": 1,
@@ -2168,9 +1848,7 @@
"hide_seconds": 1,
"label": "From Date",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -2180,17 +1858,13 @@
"hide_seconds": 1,
"label": "To Date",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_140",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"allow_on_submit": 1,
@@ -2202,9 +1876,7 @@
"no_copy": 1,
"options": "Auto Repeat",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"allow_on_submit": 1,
@@ -2213,9 +1885,7 @@
"fieldtype": "Button",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Update Auto Repeat Reference",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Update Auto Repeat Reference"
},
{
"fieldname": "against_income_account",
@@ -2228,9 +1898,7 @@
"oldfieldname": "against_income_account",
"oldfieldtype": "Small Text",
"print_hide": 1,
- "report_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "report_hide": 1
},
{
"collapsible": 1,
@@ -2238,17 +1906,13 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Accounting Dimensions",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Accounting Dimensions"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break",
"hide_days": 1,
- "hide_seconds": 1,
- "show_days": 1,
- "show_seconds": 1
+ "hide_seconds": 1
},
{
"default": "0",
@@ -2256,9 +1920,7 @@
"fieldname": "is_consolidated",
"fieldtype": "Check",
"label": "Is Consolidated",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "0",
@@ -2268,28 +1930,23 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Is Internal Customer",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fetch_from": "company.tax_id",
"fieldname": "company_tax_id",
"fieldtype": "Data",
"label": "Company Tax ID",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:doc.is_internal_customer",
"description": "Unrealized Profit / Loss account for intra-company transfers",
"fieldname": "unrealized_profit_loss_account",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Unrealized Profit / Loss Account",
- "options": "Account",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Account"
},
{
"depends_on": "eval:doc.is_internal_customer",
@@ -2297,34 +1954,28 @@
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_55",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.is_internal_customer && doc.update_stock",
"fieldname": "set_target_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Set Target Warehouse",
- "options": "Warehouse",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Warehouse"
},
{
"default": "0",
"fieldname": "is_debit_note",
"fieldtype": "Check",
- "label": "Is Debit Note",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Is Debit Note"
},
{
"default": "0",
@@ -2360,9 +2011,14 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Ignore Default Payment Terms Template",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
+ },
+ {
+ "fieldname": "total_billing_hours",
+ "fieldtype": "Float",
+ "label": "Total Billing Hours",
+ "print_hide": 1,
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
@@ -2375,11 +2031,12 @@
"link_fieldname": "consolidated_invoice"
}
],
- "modified": "2021-08-06 23:02:20.445127",
+ "modified": "2021-10-02 03:36:10.251715",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3b8cba2..d909814 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -2,34 +2,50 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-import frappe.defaults
-from frappe.utils import cint, flt, getdate, add_days, add_months, cstr, nowdate, get_link_to_form, formatdate
+
+import frappe
from frappe import _, msgprint, throw
-from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
-from frappe.model.mapper import get_mapped_doc
-from erpnext.controllers.selling_controller import SellingController
-from erpnext.accounts.utils import get_account_currency
-from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
-from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
-from erpnext.assets.doctype.asset.depreciation \
- import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, post_depreciation_entries
-from erpnext.stock.doctype.batch.batch import set_batch_nos
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
-from erpnext.setup.doctype.company.company import update_company_current_month_sales
-from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
-from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
- get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
-from erpnext.accounts.deferred_revenue import validate_service_stop_date
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
-from frappe.model.utils import get_fetch_values
from frappe.contacts.doctype.address.address import get_address_display
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
-
-from erpnext.healthcare.utils import manage_invoice_submit_cancel
-
+from frappe.model.mapper import get_mapped_doc
+from frappe.model.utils import get_fetch_values
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ cstr,
+ flt,
+ formatdate,
+ get_link_to_form,
+ getdate,
+ nowdate,
+)
from six import iteritems
+import erpnext
+from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
+ get_loyalty_program_details_with_points,
+ validate_loyalty_points,
+)
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
+ get_party_tax_withholding_details,
+)
+from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
+from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
+from erpnext.accounts.utils import get_account_currency
+from erpnext.assets.doctype.asset.depreciation import (
+ get_disposal_account_and_cost_center,
+ get_gl_entries_on_asset_disposal,
+ get_gl_entries_on_asset_regain,
+ post_depreciation_entries,
+)
+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
+
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
}
@@ -216,9 +232,6 @@
if self.update_stock == 1:
self.repost_future_sle_and_gle()
- if self.update_stock == 1:
- self.repost_future_sle_and_gle()
-
if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
@@ -246,12 +259,7 @@
if self.redeem_loyalty_points and not self.is_consolidated and self.loyalty_points:
self.apply_loyalty_points()
- # Healthcare Service Invoice.
- domain_settings = frappe.get_doc('Domain Settings')
- active_domains = [d.domain for d in domain_settings.active_domains]
-
- if "Healthcare" in active_domains:
- manage_invoice_submit_cancel(self, "on_submit")
+ self.process_common_party_accounting()
def validate_pos_return(self):
@@ -285,8 +293,6 @@
def before_cancel(self):
self.check_if_consolidated_invoice()
-
- super(SalesInvoice, self).before_cancel()
self.update_time_sheet(None)
def on_cancel(self):
@@ -335,12 +341,6 @@
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
- # Healthcare Service Invoice.
- domain_settings = frappe.get_doc('Domain Settings')
- active_domains = [d.domain for d in domain_settings.active_domains]
-
- if "Healthcare" in active_domains:
- manage_invoice_submit_cancel(self, "on_cancel")
self.unlink_sales_invoice_from_timesheets()
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
@@ -478,8 +478,11 @@
if cint(self.is_pos) != 1:
return
- from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
- if not self.pos_profile:
+ if not self.account_for_change_amount:
+ 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
@@ -492,9 +495,6 @@
if not self.get('payments') and not for_validate:
update_multi_mode_option(self, pos)
- if not self.account_for_change_amount:
- self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account')
-
if pos:
if not for_validate:
self.tax_category = pos.get("tax_category")
@@ -755,7 +755,7 @@
if self.project:
for data in get_projectwise_timesheet_data(self.project):
self.append('timesheets', {
- 'time_sheet': data.parent,
+ 'time_sheet': data.time_sheet,
'billing_hours': data.billing_hours,
'billing_amount': data.billing_amount,
'timesheet_detail': data.name,
@@ -766,12 +766,11 @@
self.calculate_billing_amount_for_timesheet()
def calculate_billing_amount_for_timesheet(self):
- total_billing_amount = 0.0
- for data in self.timesheets:
- if data.billing_amount:
- total_billing_amount += data.billing_amount
+ def timesheet_sum(field):
+ return sum((ts.get(field) or 0.0) for ts in self.timesheets)
- self.total_billing_amount = total_billing_amount
+ self.total_billing_amount = timesheet_sum("billing_amount")
+ self.total_billing_hours = timesheet_sum("billing_hours")
def get_warehouse(self):
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
@@ -888,8 +887,10 @@
)
def make_tax_gl_entries(self, gl_entries):
+ enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
+
for tax in self.get("taxes"):
- amount, base_amount = self.get_tax_amounts(tax, self.enable_discount_accounting)
+ amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
if flt(tax.base_tax_amount_after_discount_amount):
account_currency = get_account_currency(tax.account_head)
@@ -920,6 +921,7 @@
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'))
for item in self.get("items"):
if flt(item.base_net_amount, item.precision("base_net_amount")):
@@ -933,7 +935,7 @@
if asset.calculate_depreciation:
self.reset_depreciation_schedule(asset)
-
+
else:
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
item.base_net_amount, item.finance_book)
@@ -954,7 +956,7 @@
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, self.enable_discount_accounting)
+ amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
account_currency = get_account_currency(income_account)
gl_entries.append(
@@ -980,7 +982,7 @@
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),
+ "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
title=_("Missing Asset")
)
@@ -997,7 +999,7 @@
asset.flags.ignore_validate_update_after_submit = True
asset.prepare_depreciation_data(self.posting_date)
asset.save()
-
+
post_depreciation_entries(self.posting_date)
def reset_depreciation_schedule(self, asset):
@@ -1037,7 +1039,7 @@
finance_book = schedule.finance_book
else:
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):
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
@@ -1047,17 +1049,17 @@
def get_posting_date_of_sales_invoice(self):
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
+ # 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'):
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))
-
+
if orginal_schedule_date == posting_date_of_original_invoice:
return True
return False
-
+
@property
def enable_discount_accounting(self):
if not hasattr(self, "_enable_discount_accounting"):
@@ -1372,8 +1374,10 @@
# redeem the loyalty points.
def apply_loyalty_points(self):
- from erpnext.accounts.doctype.loyalty_point_entry.loyalty_point_entry \
- import get_loyalty_point_entries, get_redemption_details
+ from erpnext.accounts.doctype.loyalty_point_entry.loyalty_point_entry import (
+ get_loyalty_point_entries,
+ get_redemption_details,
+ )
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)
@@ -1408,59 +1412,13 @@
if points_to_redeem < 1: # since points_to_redeem is integer
break
- # Healthcare
- @frappe.whitelist()
- def set_healthcare_services(self, checked_values):
- self.set("items", [])
- from erpnext.stock.get_item_details import get_item_details
- for checked_item in checked_values:
- item_line = self.append("items", {})
- price_list, price_list_currency = frappe.db.get_values("Price List", {"selling": 1}, ['name', 'currency'])[0]
- args = {
- 'doctype': "Sales Invoice",
- 'item_code': checked_item['item'],
- 'company': self.company,
- 'customer': frappe.db.get_value("Patient", self.patient, "customer"),
- 'selling_price_list': price_list,
- 'price_list_currency': price_list_currency,
- 'plc_conversion_rate': 1.0,
- 'conversion_rate': 1.0
- }
- item_details = get_item_details(args)
- item_line.item_code = checked_item['item']
- item_line.qty = 1
- if checked_item['qty']:
- item_line.qty = checked_item['qty']
- if checked_item['rate']:
- item_line.rate = checked_item['rate']
- else:
- item_line.rate = item_details.price_list_rate
- item_line.amount = float(item_line.rate) * float(item_line.qty)
- if checked_item['income_account']:
- item_line.income_account = checked_item['income_account']
- if checked_item['dt']:
- item_line.reference_dt = checked_item['dt']
- if checked_item['dn']:
- item_line.reference_dn = checked_item['dn']
- if checked_item['description']:
- item_line.description = checked_item['description']
-
- self.set_missing_values(for_validate = True)
-
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):
self.status = 'Draft'
return
- precision = self.precision("outstanding_amount")
- outstanding_amount = flt(self.outstanding_amount, precision)
- due_date = getdate(self.due_date)
- nowdate = getdate()
-
- discounting_status = None
- if self.is_discounted:
- discountng_status = get_discounting_status(self.name)
+ outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
if not status:
if self.docstatus == 2:
@@ -1468,15 +1426,13 @@
elif self.docstatus == 1:
if self.is_internal_transfer():
self.status = 'Internal Transfer'
- elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
- self.status = "Overdue and Discounted"
- elif outstanding_amount > 0 and due_date < nowdate:
+ elif is_overdue(self):
self.status = "Overdue"
- elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed':
- self.status = "Unpaid and Discounted"
- elif outstanding_amount > 0 and due_date >= nowdate:
+ elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_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 credit note issued against invoice
+ # 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}):
self.status = "Credit Note Issued"
elif self.is_return == 1:
@@ -1485,12 +1441,42 @@
self.status = "Paid"
else:
self.status = "Submitted"
+
+ if (
+ self.status in ("Unpaid", "Partly Paid", "Overdue")
+ and self.is_discounted
+ and get_discounting_status(self.name) == "Disbursed"
+ ):
+ self.status += " and Discounted"
+
else:
self.status = "Draft"
if update:
self.db_set('status', self.status, update_modified = update_modified)
+def is_overdue(doc):
+ outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
+
+ if outstanding_amount <= 0:
+ return
+
+ grand_total = flt(doc.grand_total, doc.precision("grand_total"))
+ nowdate = getdate()
+ if doc.payment_schedule:
+ # calculate payable amount till date
+ payable_amount = sum(
+ payment.payment_amount
+ for payment in doc.payment_schedule
+ if getdate(payment.due_date) < nowdate
+ )
+
+ if (grand_total - outstanding_amount) < payable_amount:
+ return True
+
+ elif getdate(doc.due_date) < nowdate:
+ return True
+
def get_discounting_status(sales_invoice):
status = None
@@ -2006,7 +1992,11 @@
@frappe.whitelist()
def create_dunning(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
- from erpnext.accounts.doctype.dunning.dunning import get_dunning_letter_text, calculate_interest_and_amount
+
+ from erpnext.accounts.doctype.dunning.dunning import (
+ 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
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
index f106928..64b35b2 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'sales_invoice',
@@ -33,4 +35,4 @@
'items': ['Auto Repeat']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
index 1a01cb5..06e6f51 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
@@ -6,18 +6,20 @@
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency", "is_return"],
get_indicator: function(doc) {
- var status_color = {
+ const status_colors = {
"Draft": "grey",
"Unpaid": "orange",
"Paid": "green",
"Return": "gray",
"Credit Note Issued": "gray",
"Unpaid and Discounted": "orange",
+ "Partly Paid and Discounted": "yellow",
"Overdue and Discounted": "red",
"Overdue": "red",
+ "Partly Paid": "yellow",
"Internal Transfer": "darkgrey"
};
- return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
+ return [__(doc.status), status_colors[doc.status], "status,=,"+doc.status];
},
right_column: "grand_total"
};
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 54a8852..f492a03 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2,31 +2,40 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+import copy
+import unittest
-import unittest, copy, time
-from frappe.utils import nowdate, flt, getdate, cint, add_days, add_months
+import frappe
from frappe.model.dynamic_links import get_dynamic_link_map
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
-from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
-from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import WarehouseMissingError
-from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
-from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
-from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
-from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
-from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname
-from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
-from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
-from erpnext.stock.doctype.item.test_item import create_item
+from frappe.utils import add_days, flt, getdate, nowdate
from six import iteritems
+
+import erpnext
+from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
+from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
+from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import WarehouseMissingError
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ unlink_payment_on_cancel_of_invoice,
+)
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
+from erpnext.accounts.utils import PaymentEntryUnlinkError
+from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
+from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
+from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
+from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.regional.india.utils import get_ewb_data
-from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
+from erpnext.stock.doctype.stock_entry.test_stock_entry import (
+ get_qty_after_transaction,
+ make_stock_entry,
+)
from erpnext.stock.utils import get_incoming_rate
+
class TestSalesInvoice(unittest.TestCase):
def make(self):
w = frappe.copy_doc(test_records[0])
@@ -124,6 +133,7 @@
def test_payment_entry_unlink_against_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+
si = frappe.copy_doc(test_records[0])
si.is_pos = 0
si.insert()
@@ -136,7 +146,7 @@
pe.paid_to_account_currency = si.currency
pe.source_exchange_rate = 1
pe.target_exchange_rate = 1
- pe.paid_amount = si.grand_total
+ pe.paid_amount = si.outstanding_amount
pe.insert()
pe.submit()
@@ -145,6 +155,43 @@
self.assertRaises(frappe.LinkExistsError, si.cancel)
unlink_payment_on_cancel_of_invoice()
+ def test_payment_entry_unlink_against_standalone_credit_note(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+
+ si1 = create_sales_invoice(rate=1000)
+ 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': si3.name,
+ 'total_amount': si3.grand_total,
+ 'outstanding_amount': si3.outstanding_amount,
+ 'allocated_amount': si3.outstanding_amount
+ })
+
+ pe.reference_no = 'Test001'
+ pe.reference_date = nowdate()
+ pe.save()
+ pe.submit()
+
+ si2.load_from_db()
+ si2.cancel()
+
+ 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"
@@ -640,8 +687,9 @@
def test_payment(self):
w = self.make()
- from erpnext.accounts.doctype.journal_entry.test_journal_entry \
- import test_records as jv_test_records
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
+ test_records as jv_test_records,
+ )
jv = frappe.get_doc(frappe.copy_doc(jv_test_records[0]))
jv.get("accounts")[0].reference_type = w.doctype
@@ -907,16 +955,18 @@
def _insert_purchase_receipt(self):
- from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import test_records \
- as pr_test_records
+ 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()
pr.submit()
def _insert_delivery_note(self):
- from erpnext.stock.doctype.delivery_note.test_delivery_note import test_records \
- as dn_test_records
+ 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()
@@ -924,8 +974,9 @@
return dn
def test_sales_invoice_with_advance(self):
- from erpnext.accounts.doctype.journal_entry.test_journal_entry \
- import test_records as jv_test_records
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
+ test_records as jv_test_records,
+ )
jv = frappe.copy_doc(jv_test_records[0])
jv.insert()
@@ -957,8 +1008,8 @@
si.cancel()
def test_serialized(self):
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
se = make_serialized_item()
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
@@ -1011,9 +1062,9 @@
check if the sales invoice item serial numbers and the delivery note items
serial numbers are same
"""
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
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.doctype.stock_entry.test_stock_entry import make_serialized_item
se = make_serialized_item()
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
@@ -1103,6 +1154,18 @@
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')
+
+ 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)
+
def test_discount_on_net_total(self):
si = frappe.copy_doc(test_records[2])
si.apply_discount_on = "Net Total"
@@ -1241,8 +1304,9 @@
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 test_records as jv_test_records
+ from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
+ test_records as jv_test_records,
+ )
jv = frappe.copy_doc(jv_test_records[0])
jv.accounts[0].is_advance = 'Yes'
@@ -1358,15 +1422,22 @@
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
expected_itemised_tax = {
- "999800": {
+ "_Test Item": {
"Service Tax": {
"tax_rate": 10.0,
- "tax_amount": 1500.0
+ "tax_amount": 1000.0
+ }
+ },
+ "_Test Item 2": {
+ "Service Tax": {
+ "tax_rate": 10.0,
+ "tax_amount": 500.0
}
}
}
expected_itemised_taxable_amount = {
- "999800": 15000.0
+ "_Test Item": 10000.0,
+ "_Test Item 2": 5000.0
}
self.assertEqual(itemised_tax, expected_itemised_tax)
@@ -1528,8 +1599,7 @@
self.assertEqual(expected_values[gle.account][2], gle.credit)
def test_sales_invoice_with_shipping_rule(self):
- from erpnext.accounts.doctype.shipping_rule.test_shipping_rule \
- import create_shipping_rule
+ 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")
@@ -1578,6 +1648,7 @@
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)
outstanding_amount = get_outstanding_amount(si.doctype,
@@ -1729,6 +1800,47 @@
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
+ def test_deferred_revenue_post_account_freeze_upto_by_admin(self):
+ frappe.set_user("Administrator")
+
+ frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+ frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
+
+ 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
+ item.deferred_revenue_account = deferred_account
+ item.no_of_months = 12
+ item.save()
+
+ si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_save=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"
+ si.items[0].deferred_revenue_account = deferred_account
+ 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, 'frozen_accounts_modifier', 'System Manager')
+
+ 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()
+ self.assertRaises(frappe.ValidationError, pda1.submit)
+
+ frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+ frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
+
def test_fixed_deferred_revenue(self):
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
@@ -1779,23 +1891,13 @@
acc_settings.save()
def test_inter_company_transaction(self):
+ from erpnext.selling.doctype.customer.test_customer import create_internal_customer
- 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.append("companies", {
- "company": "Wind Power LLC"
- })
-
- customer.insert()
+ create_internal_customer(
+ customer_name="_Test Internal Customer",
+ represents_company="_Test Company 1",
+ allowed_to_interact_with="Wind Power LLC"
+ )
if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
supplier = frappe.get_doc({
@@ -1921,8 +2023,39 @@
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",
+ qty=1,
+ basic_rate=500
+ )
+
+ si = frappe.copy_doc(test_records[0])
+ si.update_stock = 1
+ si.set_warehouse = "Finished Goods - _TC"
+ si.set_target_warehouse = "Stores - _TC"
+ si.get("items")[0].warehouse = "Finished Goods - _TC"
+ si.get("items")[0].target_warehouse = "Stores - _TC"
+ si.insert()
+ si.submit()
+
+ 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)
+ self.assertEqual(sum(d.actual_qty for d in sles), 0.0)
+
+ # tear down
+ si.cancel()
+ se.cancel()
+
def test_internal_transfer_gl_entry(self):
## 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")
@@ -2014,7 +2147,7 @@
data = get_ewb_data("Sales Invoice", [si.name])
- self.assertEqual(data['version'], '1.0.1118')
+ self.assertEqual(data['version'], '1.0.0421')
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company')
self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer')
@@ -2027,54 +2160,6 @@
self.assertEqual(data['billLists'][0]['actualFromStateCode'],7)
self.assertEqual(data['billLists'][0]['fromStateCode'],27)
- def test_einvoice_submission_without_irn(self):
- # init
- einvoice_settings = frappe.get_doc('E Invoice Settings')
- einvoice_settings.enable = 1
- einvoice_settings.applicable_from = nowdate()
- einvoice_settings.append('credentials', {
- 'company': '_Test Company',
- 'gstin': '27AAECE4835E1ZR',
- 'username': 'test',
- 'password': 'test'
- })
- einvoice_settings.save()
-
- country = frappe.flags.country
- frappe.flags.country = 'India'
-
- si = make_sales_invoice_for_ewaybill()
- self.assertRaises(frappe.ValidationError, si.submit)
-
- si.irn = 'test_irn'
- si.submit()
-
- # reset
- einvoice_settings = frappe.get_doc('E Invoice Settings')
- einvoice_settings.enable = 0
- frappe.flags.country = country
-
- def test_einvoice_json(self):
- from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
-
- si = get_sales_invoice_for_e_invoice()
- si.discount_amount = 100
- si.save()
-
- einvoice = make_einvoice(si)
- self.assertTrue(einvoice['EwbDtls'])
- validate_totals(einvoice)
-
- si.apply_discount_on = 'Net Total'
- si.save()
- einvoice = make_einvoice(si)
- validate_totals(einvoice)
-
- [d.set('included_in_print_rate', 1) for d in si.taxes]
- si.save()
- einvoice = make_einvoice(si)
- validate_totals(einvoice)
-
def test_item_tax_net_range(self):
item = create_item("T Shirt")
@@ -2103,14 +2188,16 @@
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
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ enable_discount_accounting,
+ )
enable_discount_accounting()
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()],
@@ -2121,12 +2208,14 @@
enable_discount_accounting(enable=0)
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
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ enable_discount_accounting,
+ )
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.apply_discount_on = "Grand Total"
si.additional_discount_account = additional_discount_account
@@ -2174,6 +2263,100 @@
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
+ def test_sales_invoice_against_supplier(self):
+ from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
+ make_customer,
+ )
+ from erpnext.buying.doctype.supplier.test_supplier import create_supplier
+
+ # create a customer
+ customer = make_customer(customer="_Test Common Supplier")
+ # create a supplier
+ supplier = create_supplier(supplier_name="_Test Common Supplier").name
+
+ # create a party link between customer & supplier
+ # set primary role as supplier
+ party_link = frappe.new_doc("Party Link")
+ party_link.primary_role = "Supplier"
+ party_link.primary_party = supplier
+ party_link.secondary_role = "Customer"
+ party_link.secondary_party = customer
+ party_link.save()
+
+ # enable common party accounting
+ 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(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')
+
+ 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)
+
+ def test_payment_statuses(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+
+ today = nowdate()
+
+ # 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.submit()
+ self.assertEqual(si.status, "Overdue")
+
+ # Test payment less than due amount
+ pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = nowdate()
+ pe.paid_amount = 1
+ pe.references[0].allocated_amount = pe.paid_amount
+ pe.submit()
+ si.reload()
+ self.assertEqual(si.status, "Overdue")
+
+ # Test Partly Paid
+ pe = frappe.copy_doc(pe)
+ pe.paid_amount = si.grand_total / 2
+ pe.references[0].allocated_amount = pe.paid_amount
+ pe.submit()
+ si.reload()
+ self.assertEqual(si.status, "Partly Paid")
+
+ # Test Paid
+ pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = nowdate()
+ pe.paid_amount = si.outstanding_amount
+ pe.submit()
+ si.reload()
+ self.assertEqual(si.status, "Paid")
+
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
si.naming_series = 'INV-2020-.#####'
@@ -2206,6 +2389,7 @@
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
address = frappe.get_doc({
"address_line1": "_Test Address Line 1",
+ "address_line2": "_Test Address Line 2",
"address_title": "_Test Address for Eway bill",
"address_type": "Billing",
"city": "_Test City",
@@ -2227,11 +2411,12 @@
address.save()
- if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
+ if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Billing'):
address = frappe.get_doc({
"address_line1": "_Test Address Line 1",
+ "address_line2": "_Test Address Line 2",
"address_title": "_Test Customer-Address for Eway bill",
- "address_type": "Shipping",
+ "address_type": "Billing",
"city": "_Test City",
"state": "Test State",
"country": "India",
@@ -2251,9 +2436,34 @@
address.save()
+ if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
+ address = frappe.get_doc({
+ "address_line1": "_Test Address Line 1",
+ "address_line2": "_Test Address Line 2",
+ "address_title": "_Test Customer-Address for Eway bill",
+ "address_type": "Shipping",
+ "city": "_Test City",
+ "state": "Test State",
+ "country": "India",
+ "doctype": "Address",
+ "is_primary_address": 1,
+ "phone": "+910000000000",
+ "gst_state": "Maharashtra",
+ "gst_state_number": "27",
+ "pincode": "410098"
+ }).insert()
+
+ address.append("links", {
+ "link_doctype": "Customer",
+ "link_name": "_Test Customer"
+ })
+
+ address.save()
+
if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'):
address = frappe.get_doc({
"address_line1": "_Test Dispatch Address Line 1",
+ "address_line2": "_Test Dispatch Address Line 2",
"address_title": "_Test Dispatch-Address for Eway bill",
"address_type": "Shipping",
"city": "_Test City",
@@ -2268,11 +2478,6 @@
"pincode": "1100101"
}).insert()
- address.append("links", {
- "link_doctype": "Company",
- "link_name": "_Test Company"
- })
-
address.save()
def make_test_transporter_for_ewaybill():
@@ -2312,7 +2517,8 @@
si.distance = 2000
si.company_address = "_Test Address for Eway bill-Billing"
- si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
+ si.customer_address = "_Test Customer-Address for Eway bill-Billing"
+ si.shipping_address_name = "_Test Customer-Address for Eway bill-Shipping"
si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping"
si.vehicle_no = "KA12KA1234"
si.gst_category = "Registered Regular"
@@ -2386,7 +2592,8 @@
"asset": args.asset or None,
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no,
- "conversion_factor": 1
+ "conversion_factor": 1,
+ "incoming_rate": args.incoming_rate or 0
})
if not args.do_not_save:
@@ -2483,29 +2690,6 @@
"row_id": 1
}]
-def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
- if not frappe.db.exists("Customer", customer_name):
- customer = frappe.get_doc({
- "customer_group": "_Test Customer Group",
- "customer_name": customer_name,
- "customer_type": "Individual",
- "doctype": "Customer",
- "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
- else:
- customer_name = frappe.db.get_value("Customer", customer_name)
-
- return customer_name
-
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
if not frappe.db.exists("Supplier", supplier_name):
supplier = frappe.get_doc({
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js
index e12ac03..61d78e1 100644
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js
@@ -40,4 +40,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js
index f1cb22a..cf2d0fb 100644
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js
+++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js
@@ -33,4 +33,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js
index 651bf0a..45d9a14 100644
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js
+++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js
@@ -54,4 +54,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js
index b959cf9..0464e45 100644
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js
+++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js
@@ -49,4 +49,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
index 2697758..af484d7 100644
--- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
+++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
@@ -42,4 +42,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
index 29422d6..f92b57a 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
+++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
@@ -98,6 +98,7 @@
"width": "120px"
},
{
+ "depends_on": "exchange_gain_loss",
"fieldname": "exchange_gain_loss",
"fieldtype": "Currency",
"label": "Exchange Gain/Loss",
@@ -105,6 +106,7 @@
"read_only": 1
},
{
+ "depends_on": "exchange_gain_loss",
"fieldname": "ref_exchange_rate",
"fieldtype": "Float",
"label": "Reference Exchange Rate",
@@ -116,7 +118,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-04 20:25:49.832052",
+ "modified": "2021-09-26 15:47:46.911595",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Advance",
diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py
index 1ec5179..ae69598 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py
+++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class SalesInvoiceAdvance(Document):
- pass
\ No newline at end of file
+ pass
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 a853679..b90f3f0 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -53,7 +53,6 @@
"column_break_24",
"base_net_rate",
"base_net_amount",
- "incoming_rate",
"drop_ship",
"delivered_by_supplier",
"accounting",
@@ -81,6 +80,7 @@
"target_warehouse",
"quality_inspection",
"batch_no",
+ "incoming_rate",
"col_break5",
"allow_zero_valuation_rate",
"serial_no",
@@ -807,12 +807,12 @@
"read_only": 1
},
{
+ "depends_on": "eval:parent.is_return && parent.update_stock && !parent.return_against",
"fieldname": "incoming_rate",
"fieldtype": "Currency",
- "label": "Incoming Rate",
+ "label": "Incoming Rate (Costing)",
"no_copy": 1,
- "print_hide": 1,
- "read_only": 1
+ "print_hide": 1
},
{
"depends_on": "eval: doc.uom != doc.stock_uom",
@@ -833,7 +833,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-08-12 20:15:42.668399",
+ "modified": "2021-08-19 13:41:53.435827",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
index a73b03a..063c591 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class SalesInvoiceItem(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
index cc0b7a6..a980ece 100644
--- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
+++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SalesInvoicePayment(Document):
pass
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
index f069e8d..69b7c12 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -7,10 +7,19 @@
"field_order": [
"activity_type",
"description",
+ "section_break_3",
+ "from_time",
+ "column_break_5",
+ "to_time",
+ "section_break_7",
"billing_hours",
+ "column_break_9",
"billing_amount",
+ "section_break_11",
"time_sheet",
- "timesheet_detail"
+ "timesheet_detail",
+ "column_break_13",
+ "project_name"
],
"fields": [
{
@@ -61,11 +70,54 @@
"in_list_view": 1,
"label": "Description",
"read_only": 1
+ },
+ {
+ "fieldname": "from_time",
+ "fieldtype": "Datetime",
+ "label": "From Time"
+ },
+ {
+ "fieldname": "to_time",
+ "fieldtype": "Datetime",
+ "label": "To Time"
+ },
+ {
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break",
+ "label": "Time"
+ },
+ {
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break",
+ "label": "Totals"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "label": "Reference"
+ },
+ {
+ "fieldname": "project_name",
+ "fieldtype": "Data",
+ "label": "Project Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_13",
+ "fieldtype": "Column Break"
}
],
"istable": 1,
"links": [],
- "modified": "2021-05-20 22:33:57.234846",
+ "modified": "2021-10-02 03:48:44.979777",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Timesheet",
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
index afc05ab..e8d4b11 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SalesInvoiceTimesheet(Document):
pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/sales_partner_item/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/sales_partner_item/__init__.py
diff --git a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json
new file mode 100644
index 0000000..c176e4d
--- /dev/null
+++ b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-06 16:17:44.329943",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "sales_partner"
+ ],
+ "fields": [
+ {
+ "fieldname": "sales_partner",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Sales Partner ",
+ "options": "Sales Partner"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 10:43:37.532095",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Partner Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py
new file mode 100644
index 0000000..97489d1
--- /dev/null
+++ b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SalesPartnerItem(Document):
+ pass
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py
index 8d1df5c..39872f3 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class SalesTaxesandCharges(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
index 0e01188..066c4ea 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js
@@ -4,26 +4,3 @@
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
{% include "erpnext/public/js/controllers/accounts.js" %}
-
-frappe.tour['Sales Taxes and Charges Template'] = [
- {
- fieldname: "title",
- title: __("Title"),
- description: __("A name by which you will identify this template. You can change this later."),
- },
- {
- fieldname: "company",
- title: __("Company"),
- description: __("Company for which this tax template will be applicable"),
- },
- {
- fieldname: "is_default",
- title: __("Is this Default?"),
- description: __("Set this template as the default for all sales transactions"),
- },
- {
- fieldname: "taxes",
- title: __("Taxes Table"),
- description: __("You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount."),
- }
-];
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 8f9eb65..0a0bb3e 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
@@ -2,11 +2,19 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
from frappe.model.document import Document
-from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax, validate_cost_center, validate_account_head
+from frappe.utils import flt
+
+from erpnext.controllers.accounts_controller import (
+ validate_account_head,
+ validate_cost_center,
+ validate_inclusive_tax,
+ validate_taxes_and_charges,
+)
+
class SalesTaxesandChargesTemplate(Document):
def validate(self):
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 d825c6f..522e282 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
@@ -21,4 +21,4 @@
'items': ['POS Profile', 'Subscription', 'Restaurant', 'Tax Rule']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js
index d02e70b..8cd42f6 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js
@@ -26,4 +26,3 @@
() => done()
]);
});
-
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 1c0c0c7..1cad412 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
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
test_records = frappe.get_test_records('Sales Taxes and Charges Template')
class TestSalesTaxesandChargesTemplate(unittest.TestCase):
diff --git a/erpnext/accounts/doctype/share_balance/share_balance.py b/erpnext/accounts/doctype/share_balance/share_balance.py
index bd165cd..0353e99 100644
--- a/erpnext/accounts/doctype/share_balance/share_balance.py
+++ b/erpnext/accounts/doctype/share_balance/share_balance.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ShareBalance(Document):
pass
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js
index 1cad4df..6317c9c 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.js
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js
@@ -115,4 +115,4 @@
frappe.set_route("Form", doc.doctype, doc.name);
}
});
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py
index 4024b81..5117ef8 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.py
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py
@@ -3,13 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.model.document import Document
-from frappe.model.naming import make_autoname
from frappe.exceptions import ValidationError
+from frappe.model.document import Document
+from frappe.model.naming import make_autoname
from frappe.utils import nowdate
+
class ShareDontExists(ValidationError): pass
class ShareTransfer(Document):
@@ -299,4 +301,4 @@
"party": credit_applicant,
})
journal_entry.set("accounts", account_amt_list)
- return journal_entry.as_dict()
\ No newline at end of file
+ return journal_entry.as_dict()
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.js b/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
deleted file mode 100644
index e5530fa..0000000
--- a/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Share Transfer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Share Transfer
- () => frappe.tests.make('Share Transfer', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
index 2ff9b02..b40e5fb 100644
--- a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
@@ -3,8 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
from erpnext.accounts.doctype.share_transfer.share_transfer import ShareDontExists
test_dependencies = ["Share Type", "Shareholder"]
diff --git a/erpnext/accounts/doctype/share_type/share_type.py b/erpnext/accounts/doctype/share_type/share_type.py
index ab4b8bc..5b133aa 100644
--- a/erpnext/accounts/doctype/share_type/share_type.py
+++ b/erpnext/accounts/doctype/share_type/share_type.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ShareType(Document):
pass
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.js b/erpnext/accounts/doctype/share_type/test_share_type.js
deleted file mode 100644
index 620afa2..0000000
--- a/erpnext/accounts/doctype/share_type/test_share_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Share Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Share Type
- () => frappe.tests.make('Share Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.py b/erpnext/accounts/doctype/share_type/test_share_type.py
index 1c1f051..a6f8d61 100644
--- a/erpnext/accounts/doctype/share_type/test_share_type.py
+++ b/erpnext/accounts/doctype/share_type/test_share_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestShareType(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.py b/erpnext/accounts/doctype/shareholder/shareholder.py
index c507fcf..12c50c8 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder.py
+++ b/erpnext/accounts/doctype/shareholder/shareholder.py
@@ -3,9 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
+from frappe.contacts.address_and_contact import (
+ delete_contact_and_address,
+ load_address_and_contact,
+)
from frappe.model.document import Document
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+
class Shareholder(Document):
def onload(self):
diff --git a/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py b/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
index 3b77fd5..0084f25 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
+++ b/erpnext/accounts/doctype/shareholder/shareholder_dashboard.py
@@ -1,7 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.js b/erpnext/accounts/doctype/shareholder/test_shareholder.js
deleted file mode 100644
index 61c5312..0000000
--- a/erpnext/accounts/doctype/shareholder/test_shareholder.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shareholder", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shareholder
- () => frappe.tests.make('Shareholder', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.py b/erpnext/accounts/doctype/shareholder/test_shareholder.py
index 9ce0093..6790fdd 100644
--- a/erpnext/accounts/doctype/shareholder/test_shareholder.py
+++ b/erpnext/accounts/doctype/shareholder/test_shareholder.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestShareholder(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
index d32a348..2852101 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py
@@ -4,10 +4,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _, msgprint, throw
-from frappe.utils import flt, fmt_money
from frappe.model.document import Document
+from frappe.utils import flt, fmt_money
+
+import erpnext
+
class OverlappingConditionError(frappe.ValidationError): pass
class FromGreaterThanToError(frappe.ValidationError): pass
diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js
index 0201f76..63ea1bf 100644
--- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js
+++ b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js
@@ -34,4 +34,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
index abc6ab8..bdd9be3 100644
--- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
+++ b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.py
@@ -2,9 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.accounts.doctype.shipping_rule.shipping_rule import FromGreaterThanToError, ManyBlankToValuesError, OverlappingConditionError
+
+import frappe
+
+from erpnext.accounts.doctype.shipping_rule.shipping_rule import (
+ FromGreaterThanToError,
+ ManyBlankToValuesError,
+ OverlappingConditionError,
+)
test_records = frappe.get_test_records('Shipping Rule')
diff --git a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js b/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js
index ab1b77c..f3668b8 100644
--- a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js
+++ b/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js
@@ -34,4 +34,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py b/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py
index dab59db..66cd269 100644
--- a/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py
+++ b/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ShippingRuleCondition(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py
index b9646cf..9576acd 100644
--- a/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py
+++ b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ShippingRuleCountry(Document):
pass
diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py
index 4bd8c65..d9a3bcc 100644
--- a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py
+++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class SouthAfricaVATAccount(Document):
pass
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 7c4ff73..8171b3b 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -6,13 +6,27 @@
from __future__ import unicode_literals
import frappe
-import erpnext
from frappe import _
from frappe.model.document import Document
-from frappe.utils.data import nowdate, getdate, cstr, cint, add_days, date_diff, get_last_day, add_to_date, flt
-from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+from frappe.utils.data import (
+ add_days,
+ add_to_date,
+ cint,
+ cstr,
+ date_diff,
+ flt,
+ get_last_day,
+ getdate,
+ nowdate,
+)
+
+import erpnext
from erpnext import get_default_company
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
+from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
+
class Subscription(Document):
def before_insert(self):
@@ -367,21 +381,26 @@
)
# Discounts
- if self.additional_discount_percentage:
- invoice.additional_discount_percentage = self.additional_discount_percentage
+ if self.is_trialling():
+ invoice.additional_discount_percentage = 100
+ else:
+ if self.additional_discount_percentage:
+ invoice.additional_discount_percentage = self.additional_discount_percentage
- if self.additional_discount_amount:
- invoice.discount_amount = self.additional_discount_amount
+ if self.additional_discount_amount:
+ invoice.discount_amount = self.additional_discount_amount
- 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'
+ 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'
# Subscription period
invoice.from_date = self.current_invoice_start
invoice.to_date = self.current_invoice_end
invoice.flags.ignore_mandatory = True
+
+ invoice.set_missing_values()
invoice.save()
if self.submit_invoice:
diff --git a/erpnext/accounts/doctype/subscription/subscription_list.js b/erpnext/accounts/doctype/subscription/subscription_list.js
index c7325fb..6490ff3 100644
--- a/erpnext/accounts/doctype/subscription/subscription_list.js
+++ b/erpnext/accounts/doctype/subscription/subscription_list.js
@@ -14,4 +14,4 @@
return [__("Cancelled"), "gray"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 7c58e98..e2cf4d5 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -6,9 +6,18 @@
import unittest
import frappe
+from frappe.utils.data import (
+ add_days,
+ add_months,
+ add_to_date,
+ date_diff,
+ flt,
+ get_date_str,
+ nowdate,
+)
+
from erpnext.accounts.doctype.subscription.subscription import get_prorata_factor
-from frappe.utils.data import (nowdate, add_days, add_to_date, add_months, date_diff, flt, get_date_str,
- get_first_day, get_last_day)
+
def create_plan():
if not frappe.db.exists('Subscription Plan', '_Test Plan Name'):
@@ -630,5 +639,3 @@
subscription.process()
self.assertEqual(len(subscription.invoices), 1)
-
-
diff --git a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py
index 6f459b4..687c94c 100644
--- a/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py
+++ b/erpnext/accounts/doctype/subscription_invoice/subscription_invoice.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SubscriptionInvoice(Document):
pass
diff --git a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.js b/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.js
deleted file mode 100644
index 15d3df2..0000000
--- a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription Invoice", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Subscription Invoice
- () => frappe.tests.make('Subscription Invoice', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py b/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py
index e60a4ee..2cc3038 100644
--- a/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py
+++ b/erpnext/accounts/doctype/subscription_invoice/test_subscription_invoice.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSubscriptionInvoice(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js
index aaa32cf..7d6f2ae 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js
@@ -6,4 +6,4 @@
frm.toggle_reqd("cost", frm.doc.price_determination === 'Fixed rate');
frm.toggle_reqd("price_list", frm.doc.price_determination === 'Based on price list');
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
index 771611a..878ae09 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
@@ -21,7 +21,7 @@
"column_break_13",
"billing_interval_count",
"payment_plan_section",
- "payment_plan_id",
+ "product_price_id",
"column_break_16",
"payment_gateway",
"accounting_dimensions_section",
@@ -115,11 +115,6 @@
"label": "Payment Plan"
},
{
- "fieldname": "payment_plan_id",
- "fieldtype": "Data",
- "label": "Payment Plan"
- },
- {
"fieldname": "column_break_16",
"fieldtype": "Column Break"
},
@@ -144,10 +139,15 @@
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
+ },
+ {
+ "fieldname": "product_price_id",
+ "fieldtype": "Data",
+ "label": "Product Price ID"
}
],
"links": [],
- "modified": "2021-08-09 10:53:44.205774",
+ "modified": "2021-08-13 10:53:44.205774",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Plan",
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
index 1ca442a..c7bb58c 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import get_first_day, get_last_day, date_diff, flt, getdate
from frappe.model.document import Document
+from frappe.utils import date_diff, flt, get_first_day, get_last_day, getdate
+
from erpnext.utilities.product import get_price
+
class SubscriptionPlan(Document):
def validate(self):
self.validate_interval_count()
@@ -54,4 +57,4 @@
cost -= (plan.cost * prorate_factor)
- return cost
\ No newline at end of file
+ return cost
diff --git a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.js b/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.js
deleted file mode 100644
index 3ceb9a6..0000000
--- a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription Plan", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Subscription Plan
- () => frappe.tests.make('Subscription Plan', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py
index 73afbf6..ba99763 100644
--- a/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/test_subscription_plan.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSubscriptionPlan(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.py b/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.py
index 1d9606f..a63a277 100644
--- a/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.py
+++ b/erpnext/accounts/doctype/subscription_plan_detail/subscription_plan_detail.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SubscriptionPlanDetail(Document):
pass
diff --git a/erpnext/accounts/doctype/subscription_settings/subscription_settings.py b/erpnext/accounts/doctype/subscription_settings/subscription_settings.py
index cc378e4..5473583 100644
--- a/erpnext/accounts/doctype/subscription_settings/subscription_settings.py
+++ b/erpnext/accounts/doctype/subscription_settings/subscription_settings.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SubscriptionSettings(Document):
pass
diff --git a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.js b/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.js
deleted file mode 100644
index 5a751ea..0000000
--- a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Subscription Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Subscription Settings
- () => frappe.tests.make('Subscription Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py b/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py
index 82c7e1d..5875ee0 100644
--- a/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py
+++ b/erpnext/accounts/doctype/subscription_settings/test_subscription_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSubscriptionSettings(unittest.TestCase):
pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/supplier_group_item/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/supplier_group_item/__init__.py
diff --git a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json
new file mode 100644
index 0000000..67fac45
--- /dev/null
+++ b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-06 16:19:22.040795",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "supplier_group"
+ ],
+ "fields": [
+ {
+ "fieldname": "supplier_group",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Supplier Group",
+ "options": "Supplier Group"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 10:43:59.877938",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Supplier Group Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py
new file mode 100644
index 0000000..61c16fe
--- /dev/null
+++ b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SupplierGroupItem(Document):
+ pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/supplier_item/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/supplier_item/__init__.py
diff --git a/erpnext/accounts/doctype/supplier_item/supplier_item.json b/erpnext/accounts/doctype/supplier_item/supplier_item.json
new file mode 100644
index 0000000..95c4dc6
--- /dev/null
+++ b/erpnext/accounts/doctype/supplier_item/supplier_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-06 16:18:54.758468",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "supplier"
+ ],
+ "fields": [
+ {
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Supplier",
+ "options": "Supplier"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 10:44:09.707778",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Supplier Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/supplier_item/supplier_item.py b/erpnext/accounts/doctype/supplier_item/supplier_item.py
new file mode 100644
index 0000000..2105b1d
--- /dev/null
+++ b/erpnext/accounts/doctype/supplier_item/supplier_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SupplierItem(Document):
+ pass
diff --git a/erpnext/accounts/doctype/tax_category/tax_category.py b/erpnext/accounts/doctype/tax_category/tax_category.py
index 2870e32..df31a5e 100644
--- a/erpnext/accounts/doctype/tax_category/tax_category.py
+++ b/erpnext/accounts/doctype/tax_category/tax_category.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TaxCategory(Document):
pass
diff --git a/erpnext/accounts/doctype/tax_category/test_tax_category.js b/erpnext/accounts/doctype/tax_category/test_tax_category.js
deleted file mode 100644
index 5142456..0000000
--- a/erpnext/accounts/doctype/tax_category/test_tax_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tax Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Tax Category
- () => frappe.tests.make('Tax Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/tax_category/test_tax_category.py b/erpnext/accounts/doctype/tax_category/test_tax_category.py
index 548d008..9093105 100644
--- a/erpnext/accounts/doctype/tax_category/test_tax_category.py
+++ b/erpnext/accounts/doctype/tax_category/test_tax_category.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestTaxCategory(unittest.TestCase):
pass
diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py
index e4ebc6d..150498d 100644
--- a/erpnext/accounts/doctype/tax_rule/tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py
@@ -3,19 +3,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from past.builtins import cmp
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cstr, cint
-from frappe.contacts.doctype.address.address import get_default_address
-from frappe.utils.nestedset import get_root_of
-from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
import functools
+import frappe
+from frappe import _
+from frappe.contacts.doctype.address.address import get_default_address
+from frappe.model.document import Document
+from frappe.utils import cint, cstr
+from frappe.utils.nestedset import get_root_of
+from past.builtins import cmp
from six import iteritems
+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
@@ -188,4 +190,4 @@
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))
- return condition
\ No newline at end of file
+ return condition
diff --git a/erpnext/accounts/doctype/tax_rule/test_tax_rule.js b/erpnext/accounts/doctype/tax_rule/test_tax_rule.js
deleted file mode 100644
index 72d177d..0000000
--- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tax Rule", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Tax Rule
- () => frappe.tests.make('Tax Rule', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
index cf72268..f937274 100644
--- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
@@ -3,16 +3,19 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.accounts.doctype.tax_rule.tax_rule import IncorrectCustomerGroup, IncorrectSupplierType, ConflictingTaxRule, get_tax_template
-from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
+
+import frappe
+
+from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule, get_tax_template
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')
from six import iteritems
+
class TestTaxRule(unittest.TestCase):
@classmethod
def setUpClass(cls):
diff --git a/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.py b/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.py
index 76e3fa3..fd19482 100644
--- a/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.py
+++ b/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TaxWithholdingAccount(Document):
pass
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 481ef28..16ef5fc 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -3,14 +3,32 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, getdate, cint
-from erpnext.accounts.utils import get_fiscal_year
+from frappe.utils import cint, getdate
+
class TaxWithholdingCategory(Document):
- pass
+ def validate(self):
+ self.validate_dates()
+ self.validate_thresholds()
+
+ def validate_dates(self):
+ last_date = None
+ 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))
+
+ # validate overlapping of dates
+ if last_date and getdate(d.to_date) < getdate(last_date):
+ frappe.throw(_("Row #{0}: Dates overlapping with other row").format(d.idx))
+
+ def validate_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))
def get_party_details(inv):
party_type, party = '', ''
@@ -49,8 +67,8 @@
if not parties:
parties.append(party)
- fiscal_year = get_fiscal_year(inv.get('posting_date') or inv.get('transaction_date'), company=inv.company)
- tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
+ 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}')
@@ -64,7 +82,7 @@
tax_amount, tax_deducted = get_tax_amount(
party_type, parties,
inv, tax_details,
- fiscal_year, pan_no
+ posting_date, pan_no
)
if party_type == 'Supplier':
@@ -74,16 +92,19 @@
return tax_row
-def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
+def get_tax_withholding_details(tax_withholding_category, posting_date, company):
tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
- tax_rate_detail = get_tax_withholding_rates(tax_withholding, fiscal_year)
+ tax_rate_detail = get_tax_withholding_rates(tax_withholding, posting_date)
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,
@@ -92,13 +113,13 @@
"round_off_tax_amount": tax_withholding.round_off_tax_amount
})
-def get_tax_withholding_rates(tax_withholding, fiscal_year):
+def get_tax_withholding_rates(tax_withholding, posting_date):
# returns the row that matches with the fiscal year from posting date
for rate in tax_withholding.rates:
- if rate.fiscal_year == fiscal_year:
+ if getdate(rate.from_date) <= getdate(posting_date) <= getdate(rate.to_date):
return rate
- frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
+ 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 = {
@@ -140,38 +161,38 @@
"account_head": tax_details.account_head
}
-def get_lower_deduction_certificate(fiscal_year, pan_no):
- ldc_name = frappe.db.get_value('Lower Deduction Certificate', { 'pan_no': pan_no, 'fiscal_year': fiscal_year }, 'name')
+def get_lower_deduction_certificate(tax_details, pan_no):
+ ldc_name = frappe.db.get_value('Lower Deduction Certificate',
+ {
+ 'pan_no': pan_no,
+ '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)
-def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
- fiscal_year = fiscal_year_details[0]
-
-
- vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
- advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+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)
taxable_vouchers = vouchers + advance_vouchers
tax_deducted = 0
if taxable_vouchers:
- tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
+ tax_deducted = get_deducted_tax(taxable_vouchers, tax_details)
tax_amount = 0
- posting_date = inv.get('posting_date') or inv.get('transaction_date')
if party_type == 'Supplier':
- ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
+ 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, fiscal_year, 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,
- fiscal_year_details, tax_deducted, vouchers
- )
+ tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
elif party_type == 'Customer':
if tax_deducted:
@@ -180,29 +201,47 @@
else:
# if no TCS has been charged in FY,
# then chargeable value is "prev invoices + advances" value which cross the threshold
- tax_amount = get_tcs_amount(
- parties, inv, tax_details,
- fiscal_year_details, vouchers, advance_vouchers
- )
+ tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
return tax_amount, tax_deducted
-def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'):
+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 = {
- dr_or_cr: ['>', 0],
'company': company,
- 'party_type': party_type,
- 'party': ['in', parties],
- 'fiscal_year': fiscal_year,
+ frappe.scrub(party_type): ['in', parties],
+ 'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
'is_opening': 'No',
- 'is_cancelled': 0
+ 'docstatus': 1
}
- return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
+ 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')
+ })
-def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None, to_date=None, party_type='Supplier'):
+ invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""]
+
+ journal_entries = frappe.db.sql("""
+ SELECT j.name
+ FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
+ WHERE
+ j.docstatus = 1
+ AND j.is_opening = 'No'
+ 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)
+
+ 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'):
# for advance vouchers, debit and credit is reversed
dr_or_cr = 'debit' if party_type == 'Supplier' else 'credit'
@@ -215,8 +254,6 @@
'against_voucher': ['is', 'not set']
}
- if fiscal_year:
- filters['fiscal_year'] = fiscal_year
if company:
filters['company'] = company
if from_date and to_date:
@@ -224,30 +261,32 @@
return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""]
-def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
+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],
- 'fiscal_year': fiscal_year,
+ 'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
'account': tax_details.account_head,
'voucher_no': ['in', taxable_vouchers],
}
- field = "sum(credit)"
+ field = "credit"
- return frappe.db.get_value('GL Entry', filters, field) or 0.0
+ entries = frappe.db.get_all('GL Entry', filters, pluck=field)
+ return sum(entries)
-def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
+def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
tds_amount = 0
invoice_filters = {
- 'name': ('in', vouchers),
- 'docstatus': 1
+ 'name': ('in', vouchers),
+ 'docstatus': 1,
+ 'apply_tds': 1
}
field = 'sum(net_total)'
- if not cint(tax_details.consider_party_ledger_amount):
- invoice_filters.update({'apply_tds': 1})
+ if cint(tax_details.consider_party_ledger_amount):
+ 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
@@ -260,7 +299,7 @@
supp_credit_amt += supp_jv_credit_amt
supp_credit_amt += inv.net_total
- debit_note_amount = get_debit_note_amount(parties, fiscal_year_details, 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)
@@ -282,15 +321,14 @@
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
else:
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
-
+
if cint(tax_details.round_off_tax_amount):
tds_amount = round(tds_amount)
return tds_amount
-def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
+def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
tcs_amount = 0
- fiscal_year, _, _ = fiscal_year_details
# sum of debit entries made from sales invoices
invoiced_amt = frappe.db.get_value('GL Entry', {
@@ -309,14 +347,14 @@
}, 'sum(credit)') or 0.0
# sum of credit entries made from sales invoice
- credit_note_amt = frappe.db.get_value('GL Entry', {
+ credit_note_amt = sum(frappe.db.get_all('GL Entry', {
'is_cancelled': 0,
'credit': ['>', 0],
'party': ['in', parties],
- 'fiscal_year': fiscal_year,
+ 'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
'company': inv.company,
'voucher_type': 'Sales Invoice',
- }, 'sum(credit)') or 0.0
+ }, pluck='credit'))
cumulative_threshold = tax_details.get('cumulative_threshold', 0)
@@ -335,7 +373,7 @@
return inv.grand_total - tcs_tax_row_amount
-def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total):
+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),
@@ -352,14 +390,13 @@
return tds_amount
-def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
- _, year_start_date, year_end_date = fiscal_year_details
+def get_debit_note_amount(suppliers, from_date, to_date, company=None):
filters = {
'supplier': ['in', suppliers],
'is_return': 1,
'docstatus': 1,
- 'posting_date': ['between', (year_start_date, year_end_date)]
+ 'posting_date': ['between', (from_date, to_date)]
}
fields = ['abs(sum(net_total)) as net_total']
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 d51ba65..152ee46 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,7 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
-
def get_data():
return {
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.js b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.js
deleted file mode 100644
index eab98d4..0000000
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tax Withholding Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Tax Withholding Category
- () => frappe.tests.make('Tax Withholding Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 2ba22ca..84b364b 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
@@ -3,11 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import today
+
from erpnext.accounts.utils import get_fiscal_year
-from erpnext.buying.doctype.supplier.test_supplier import create_supplier
test_dependencies = ["Supplier Group", "Customer Group"]
@@ -97,7 +98,7 @@
pi.save()
pi.submit()
invoices.append(pi)
-
+
# Second Invoice will apply TDS checked
pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000)
pi1.submit()
@@ -145,6 +146,59 @@
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")
+ 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.save()
+ pi.submit()
+ invoices.append(pi)
+
+ # Second Invoice will apply TDS checked
+ 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
+ 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")
+ invoices = []
+
+ 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.tax_withholding_category = "Test Goods Category"
+ pi1.save()
+ pi1.submit()
+ invoices.append(pi1)
+
+ self.assertEqual(pi1.taxes[0].tax_amount, 250)
+
+ #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']],
@@ -220,7 +274,8 @@
def create_records():
# create a new suppliers
- for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3']:
+ 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
@@ -282,16 +337,16 @@
}).insert()
def create_tax_with_holding_category():
- fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
-
- # Cummulative thresold
+ 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": [{
- 'fiscal_year': fiscal_year,
+ 'from_date': fiscal_year[1],
+ 'to_date': fiscal_year[2],
'tax_withholding_rate': 10,
'single_threshold': 0,
'cumulative_threshold': 30000.00
@@ -308,7 +363,8 @@
"name": "Cumulative Threshold TCS",
"category_name": "10% TCS",
"rates": [{
- 'fiscal_year': fiscal_year,
+ 'from_date': fiscal_year[1],
+ 'to_date': fiscal_year[2],
'tax_withholding_rate': 10,
'single_threshold': 0,
'cumulative_threshold': 30000.00
@@ -326,7 +382,8 @@
"name": "Single Threshold TDS",
"category_name": "10% TDS",
"rates": [{
- 'fiscal_year': fiscal_year,
+ 'from_date': fiscal_year[1],
+ 'to_date': fiscal_year[2],
'tax_withholding_rate': 10,
'single_threshold': 20000.00,
'cumulative_threshold': 0
@@ -346,7 +403,8 @@
"consider_party_ledger_amount": 1,
"tax_on_excess_amount": 1,
"rates": [{
- 'fiscal_year': fiscal_year,
+ 'from_date': fiscal_year[1],
+ 'to_date': fiscal_year[2],
'tax_withholding_rate': 10,
'single_threshold': 0,
'cumulative_threshold': 30000
@@ -356,3 +414,39 @@
'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()
+
+ 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()
diff --git a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
index 1e8194a..d2c505c 100644
--- a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
+++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json
@@ -1,202 +1,72 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-07-17 16:53:13.716665",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2018-07-17 16:53:13.716665",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "from_date",
+ "to_date",
+ "tax_withholding_rate",
+ "column_break_3",
+ "single_threshold",
+ "cumulative_threshold"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 2,
- "fieldname": "fiscal_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": "Fiscal Year",
- "length": 0,
- "no_copy": 0,
- "options": "Fiscal 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
- },
+ "columns": 1,
+ "fieldname": "tax_withholding_rate",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Tax Withholding Rate",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 2,
- "fieldname": "tax_withholding_rate",
- "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": "Tax Withholding Rate",
- "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": "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": "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
- },
+ "columns": 2,
+ "fieldname": "single_threshold",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Single Transaction Threshold"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 3,
- "fieldname": "single_threshold",
- "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": "Single Transaction Threshold",
- "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
- },
+ "columns": 3,
+ "fieldname": "cumulative_threshold",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Cumulative Transaction Threshold"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 3,
- "fieldname": "cumulative_threshold",
- "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": "Cumulative Transaction Threshold",
- "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
+ "columns": 2,
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "From Date",
+ "reqd": 1
+ },
+ {
+ "columns": 2,
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "To Date",
+ "reqd": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-07-17 17:13:09.819580",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Tax Withholding Rate",
- "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
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-31 11:42:12.213977",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Tax Withholding Rate",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
index 6e32abe..6556277 100644
--- a/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
+++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TaxWithholdingRate(Document):
pass
diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/accounts/doctype/territory_item/__init__.py
similarity index 100%
copy from erpnext/accounts/print_format/gst_e_invoice/__init__.py
copy to erpnext/accounts/doctype/territory_item/__init__.py
diff --git a/erpnext/accounts/doctype/territory_item/territory_item.json b/erpnext/accounts/doctype/territory_item/territory_item.json
new file mode 100644
index 0000000..0f0fdea
--- /dev/null
+++ b/erpnext/accounts/doctype/territory_item/territory_item.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-05-06 16:16:51.885441",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "territory"
+ ],
+ "fields": [
+ {
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Territory",
+ "options": "Territory"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 10:43:26.641030",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Territory Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/territory_item/territory_item.py b/erpnext/accounts/doctype/territory_item/territory_item.py
new file mode 100644
index 0000000..bcc02be
--- /dev/null
+++ b/erpnext/accounts/doctype/territory_item/territory_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TerritoryItem(Document):
+ pass
diff --git a/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json b/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
new file mode 100644
index 0000000..e2bf50d
--- /dev/null
+++ b/erpnext/accounts/form_tour/accounts_settings/accounts_settings.json
@@ -0,0 +1,113 @@
+{
+ "creation": "2021-06-29 17:00:18.273054",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 17:00:26.145996",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Accounts Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Accounts Settings",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.",
+ "field": "",
+ "fieldname": "over_billing_allowance",
+ "fieldtype": "Currency",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Over Billing Allowance (%)",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Over Billing Allowance Percentage"
+ },
+ {
+ "description": "Select the role that is allowed to overbill a transactions.",
+ "field": "",
+ "fieldname": "role_allowed_to_over_bill",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Role Allowed to Over Bill ",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Role Allowed to Over Bill"
+ },
+ {
+ "description": "If checked, system will unlink the payment against the respective invoice.",
+ "field": "",
+ "fieldname": "unlink_payment_on_cancellation_of_invoice",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Unlink Payment on Cancellation of Invoice",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Unlink Payment on Cancellation of Invoice"
+ },
+ {
+ "description": "Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.",
+ "field": "",
+ "fieldname": "unlink_advance_payment_on_cancelation_of_order",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Unlink Advance Payment on Cancellation of Order",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Unlink Advance Payment on Cancellation of Order"
+ },
+ {
+ "description": "Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.",
+ "field": "",
+ "fieldname": "determine_address_tax_category_from",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Determine Address Tax Category From",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Determine Address Tax Category From"
+ },
+ {
+ "description": "Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role.",
+ "field": "",
+ "fieldname": "acc_frozen_upto",
+ "fieldtype": "Date",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Accounts Frozen Till Date",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Accounts Frozen Upto"
+ },
+ {
+ "description": "Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.",
+ "field": "",
+ "fieldname": "frozen_accounts_modifier",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Role Allowed to Set Frozen Accounts and Edit Frozen Entries",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries"
+ },
+ {
+ "description": "Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.",
+ "field": "",
+ "fieldname": "credit_controller",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Credit Controller",
+ "parent_field": "",
+ "position": "Left",
+ "title": "Credit Controller"
+ }
+ ],
+ "title": "Accounts Settings"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/form_tour/purchase_invoice/purchase_invoice.json b/erpnext/accounts/form_tour/purchase_invoice/purchase_invoice.json
new file mode 100644
index 0000000..2dffcd1
--- /dev/null
+++ b/erpnext/accounts/form_tour/purchase_invoice/purchase_invoice.json
@@ -0,0 +1,96 @@
+{
+ "creation": "2021-06-29 16:31:48.558826",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 16:31:48.558826",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Invoice",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "Select Supplier",
+ "field": "",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Supplier",
+ "next_step_condition": "supplier",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Select Supplier"
+ },
+ {
+ "description": "Add items in the table",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Items",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "List of Items"
+ },
+ {
+ "child_doctype": "Purchase Invoice Item",
+ "description": "Select an item",
+ "field": "",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Item",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Select Item"
+ },
+ {
+ "child_doctype": "Purchase Invoice Item",
+ "description": "Enter the quantity",
+ "field": "",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Accepted Qty",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Enter Quantity"
+ },
+ {
+ "child_doctype": "Purchase Invoice Item",
+ "description": "Enter rate of the item",
+ "field": "",
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Rate",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Enter Rate"
+ },
+ {
+ "description": "You can add taxes here",
+ "field": "",
+ "fieldname": "taxes",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Purchase Taxes and Charges",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Select taxes"
+ }
+ ],
+ "title": "Purchase Invoice"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
new file mode 100644
index 0000000..7de9ae1
--- /dev/null
+++ b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
@@ -0,0 +1,65 @@
+{
+ "creation": "2021-08-24 12:28:18.044902",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 12:28:18.044902",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Taxes and Charges Template",
+ "owner": "Administrator",
+ "reference_doctype": "Sales Taxes and Charges Template",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "A name by which you will identify this template. You can change this later.",
+ "field": "",
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Title",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Title"
+ },
+ {
+ "description": "Company for which this tax template will be applicable",
+ "field": "",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Company",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Company"
+ },
+ {
+ "description": "Set this template as the default for all sales transactions",
+ "field": "",
+ "fieldname": "is_default",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Default",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Is this Default Tax Template?"
+ },
+ {
+ "description": "You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount.",
+ "field": "",
+ "fieldname": "taxes",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Sales Taxes and Charges",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Taxes Table"
+ }
+ ],
+ "title": "Sales Taxes and Charges Template"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 4c7c567..0cee6f5 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -2,12 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import flt, cstr, cint, comma_and, today, getdate, formatdate, now
+
+import frappe
from frappe import _
from frappe.model.meta import get_field_precision
+from frappe.utils import cint, cstr, flt, formatdate, getdate, now
+
+import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+
class ClosedAccountingPeriod(frappe.ValidationError): pass
@@ -101,7 +107,7 @@
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']
+ 'cost_center', 'against_voucher_type', 'party_type', 'project', 'finance_book']
if dimensions:
account_head_fieldnames = account_head_fieldnames + dimensions
@@ -278,13 +284,16 @@
"""
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
"""
if not adv_adj:
acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
if acc_frozen_upto:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if getdate(posting_date) <= getdate(acc_frozen_upto) \
- and not frozen_accounts_modifier in frappe.get_roles():
+ and not frozen_accounts_modifier in frappe.get_roles() or frappe.session.user == 'Administrator':
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def set_as_cancel(voucher_type, voucher_no):
diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json
index 6b5c5a1..2e0ab43 100644
--- a/erpnext/accounts/module_onboarding/accounts/accounts.json
+++ b/erpnext/accounts/module_onboarding/accounts/accounts.json
@@ -13,35 +13,35 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0,
"is_complete": 0,
- "modified": "2020-10-30 15:41:15.547225",
+ "modified": "2021-08-13 11:59:35.690443",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts",
"owner": "Administrator",
"steps": [
{
+ "step": "Company"
+ },
+ {
"step": "Chart of Accounts"
},
{
"step": "Setup Taxes"
},
{
- "step": "Create a Product"
+ "step": "Accounts Settings"
},
{
- "step": "Create a Supplier"
+ "step": "Cost Centers for Report and Budgeting"
},
{
"step": "Create Your First Purchase Invoice"
},
{
- "step": "Create a Customer"
+ "step": "Updating Opening Balances"
},
{
- "step": "Create Your First Sales Invoice"
- },
- {
- "step": "Configure Account Settings"
+ "step": "Financial Statements"
}
],
"subtitle": "Accounts, Invoices, Taxation, and more.",
diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.py b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.py
index 1bc4d18..f57de91 100644
--- a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.py
+++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_context(context):
# do your magic here
pass
diff --git a/erpnext/accounts/onboarding_step/accounts_settings/accounts_settings.json b/erpnext/accounts/onboarding_step/accounts_settings/accounts_settings.json
new file mode 100644
index 0000000..3f44a73
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/accounts_settings/accounts_settings.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Take a quick walk-through of Accounts Settings",
+ "creation": "2021-06-29 16:42:03.400731",
+ "description": "# Account Settings\n\nIn ERPNext, Accounting features are configurable as per your business needs. Accounts Settings is the place to define some of your accounting preferences like:\n\n - Credit Limit and over billing settings\n - Taxation preferences\n - Deferred accounting preferences\n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 1,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:50:06.227835",
+ "modified_by": "Administrator",
+ "name": "Accounts Settings",
+ "owner": "Administrator",
+ "reference_document": "Accounts Settings",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Accounts Settings",
+ "validate_action": 1
+}
\ No newline at end of file
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 fc49bd6..67553ba 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,10 +1,10 @@
{
- "action": "Go to Page",
- "action_label": "View Chart of Accounts",
+ "action": "Watch Video",
+ "action_label": "Learn more about Chart of Accounts",
"callback_message": "You can continue with the onboarding after exploring this page",
"callback_title": "Awesome Work",
"creation": "2020-05-13 19:58:20.928127",
- "description": "# Chart Of Accounts\n\nThe Chart of Accounts is the blueprint of the accounts in your organization.\nIt is a tree view of the names of the Accounts (Ledgers and Groups) that a Company requires to manage its books of accounts. ERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThere's a brief video tutorial about chart of accounts in the next step.",
+ "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,
"doctype": "Onboarding Step",
"idx": 0,
@@ -12,7 +12,7 @@
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-30 14:35:59.474920",
+ "modified": "2021-08-13 11:46:25.878506",
"modified_by": "Administrator",
"name": "Chart of Accounts",
"owner": "Administrator",
@@ -21,5 +21,6 @@
"show_form_tour": 0,
"show_full_form": 0,
"title": "Review Chart of Accounts",
- "validate_action": 0
+ "validate_action": 0,
+ "video_url": "https://www.youtube.com/embed/AcfMCT7wLLo"
}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/company/company.json b/erpnext/accounts/onboarding_step/company/company.json
new file mode 100644
index 0000000..4992e4d
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/company/company.json
@@ -0,0 +1,22 @@
+{
+ "action": "Go to Page",
+ "action_label": "Let's Review your Company",
+ "creation": "2021-06-29 14:47:42.497318",
+ "description": "# Company\n\nIn ERPNext, you can also create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company. \n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:43:35.767341",
+ "modified_by": "Administrator",
+ "name": "Company",
+ "owner": "Administrator",
+ "path": "app/company",
+ "reference_document": "Company",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Review Company",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/cost_centers_for_report_and_budgeting/cost_centers_for_report_and_budgeting.json b/erpnext/accounts/onboarding_step/cost_centers_for_report_and_budgeting/cost_centers_for_report_and_budgeting.json
new file mode 100644
index 0000000..252b075
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/cost_centers_for_report_and_budgeting/cost_centers_for_report_and_budgeting.json
@@ -0,0 +1,21 @@
+{
+ "action": "Go to Page",
+ "action_label": "View Cost Center Tree",
+ "creation": "2021-07-12 12:02:05.726608",
+ "description": "# Cost Centers for Budgeting and Analysis\n\nWhile your Books of Accounts are framed to fulfill statutory requirements, you can set up Cost Center and Accounting Dimensions to address your companies reporting and budgeting requirements.\n\nClick here to learn more about how <b>[Cost Center](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/cost-center)</b> and <b> [Dimensions](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/accounting-dimensions)</b> allow you to get advanced financial analytics reports from ERPNext.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:55:08.510366",
+ "modified_by": "Administrator",
+ "name": "Cost Centers for Report and Budgeting",
+ "owner": "Administrator",
+ "path": "cost-center/view/tree",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Cost Centers for Budgeting and Analysis",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json
index ddbc89e..f4e298e 100644
--- a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json
+++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json
@@ -1,14 +1,15 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Purchase Invoice",
"creation": "2020-05-14 22:10:07.049704",
- "description": "# What's a Purchase Invoice?\n\nA Purchase Invoice is a bill you receive from your Suppliers against which you need to make the payment.\nPurchase Invoice is the exact opposite of your Sales Invoice. Here you accrue expenses to your Supplier. \n\nThe following is what a typical purchase cycle looks like, however you can create a purchase invoice directly as well.\n\n![Purchase Flow](https://docs.erpnext.com/docs/assets/img/accounts/pi-flow.png)\n\n",
+ "description": "# Create your first Purchase Invoice\n\nA Purchase Invoice is a bill received from a Supplier for a product(s) or service(s) delivery to your company. You can track payables through Purchase Invoice and process Payment Entries against it.\n\nPurchase Invoices can also be created against a Purchase Order or Purchase Receipt.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-30 15:30:26.337773",
+ "modified": "2021-08-13 11:56:11.677253",
"modified_by": "Administrator",
"name": "Create Your First Purchase Invoice",
"owner": "Administrator",
diff --git a/erpnext/accounts/onboarding_step/financial_statements/financial_statements.json b/erpnext/accounts/onboarding_step/financial_statements/financial_statements.json
new file mode 100644
index 0000000..85cf9cd
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/financial_statements/financial_statements.json
@@ -0,0 +1,23 @@
+{
+ "action": "View Report",
+ "creation": "2021-07-12 12:08:47.026115",
+ "description": "# Financial Statements\n\nIn ERPNext, you can get crucial financial reports like [Balance Sheet] and [Profit and Loss] statements with a click of a button. You can run in the report for a different period and plot analytics charts premised on statement data. For more reports, check sections like Financial Statements, General Ledger, and Profitability reports.\n\n<b>[Check Accounting reports](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/accounting-reports)</b>",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:59:18.767407",
+ "modified_by": "Administrator",
+ "name": "Financial Statements",
+ "owner": "Administrator",
+ "reference_report": "General Ledger",
+ "report_description": "General Ledger",
+ "report_reference_doctype": "GL Entry",
+ "report_type": "Script Report",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Financial Statements",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
index a492201..9f4c873 100644
--- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
+++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
@@ -1,21 +1,21 @@
{
"action": "Create Entry",
- "action_label": "Make a Sales Tax Template",
+ "action_label": "Manage Sales Tax Templates",
"creation": "2020-05-13 19:29:43.844463",
- "description": "# Setting up Taxes\n\nAny sophisticated accounting system, including ERPNext will have automatic tax calculations for your transactions. These calculations are based on user defined rules in compliance to local rules and regulations.\n\nERPNext allows this via *Tax Templates*. These templates can be used in Sales Orders and Sales Invoices. Other types of charges that may apply to your invoices (like shipping, insurance etc.) can also be configured as taxes.\n\nFor Tax Accounts that you want to use in the tax templates, go to:\n\n`> Accounting > Taxes > Sales Taxes and Charges Template`\n\nYou can read more about these templates in our documentation [here](https://docs.erpnext.com/docs/user/manual/en/selling/sales-taxes-and-charges-template)\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.\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-30 14:54:18.087383",
+ "modified": "2021-08-13 11:48:37.238610",
"modified_by": "Administrator",
"name": "Setup Taxes",
"owner": "Administrator",
"reference_document": "Sales Taxes and Charges Template",
"show_form_tour": 1,
"show_full_form": 1,
- "title": "Lets create a Tax Template for Sales ",
+ "title": "Setting up Taxes",
"validate_action": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/onboarding_step/updating_opening_balances/updating_opening_balances.json b/erpnext/accounts/onboarding_step/updating_opening_balances/updating_opening_balances.json
new file mode 100644
index 0000000..c0849a4
--- /dev/null
+++ b/erpnext/accounts/onboarding_step/updating_opening_balances/updating_opening_balances.json
@@ -0,0 +1,22 @@
+{
+ "action": "Watch Video",
+ "action_label": "Learn how to update opening balances",
+ "creation": "2021-07-12 11:53:50.525030",
+ "description": "# Updating Opening Balances\n\nOnce you close the financial statement in previous accounting software, you can update the same as opening in your ERPNext's Balance Sheet accounts. This will allow you to get complete financial statements from ERPNext in the coming years, and discontinue the parallel accounting system right away.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "intro_video_url": "https://www.youtube.com/embed/U5wPIvEn-0c",
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 11:56:45.483418",
+ "modified_by": "Administrator",
+ "name": "Updating Opening Balances",
+ "owner": "Administrator",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Updating Opening Balances",
+ "validate_action": 1,
+ "video_url": "https://www.youtube.com/embed/U5wPIvEn-0c"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 329f9a9..7ea6cce 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -3,20 +3,37 @@
from __future__ import unicode_literals
-import frappe, erpnext
+import frappe
from frappe import _, msgprint, scrub
+from frappe.contacts.doctype.address.address import (
+ get_address_display,
+ get_company_address,
+ get_default_address,
+)
+from frappe.contacts.doctype.contact.contact import get_contact_details
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values
-from frappe.utils import (add_days, getdate, formatdate, date_diff,
- add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day, cint)
-from frappe.contacts.doctype.address.address import (get_address_display,
- get_default_address, get_company_address)
-from frappe.contacts.doctype.contact.contact import get_contact_details
-from erpnext.exceptions import PartyFrozen, PartyDisabled, InvalidAccountCurrency
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext import get_company_currency
+from frappe.utils import (
+ add_days,
+ add_months,
+ add_years,
+ cint,
+ cstr,
+ date_diff,
+ flt,
+ formatdate,
+ get_last_day,
+ get_timestamp,
+ getdate,
+ nowdate,
+)
+from six import iteritems
-from six import iteritems, string_types
+import erpnext
+from erpnext import get_company_currency
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
+
class DuplicatePartyAccountError(frappe.ValidationError): pass
@@ -286,6 +303,7 @@
.format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
def validate_party_accounts(doc):
+
companies = []
for account in doc.get("accounts"):
@@ -385,7 +403,7 @@
@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):
- from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
+ from erpnext.accounts.doctype.tax_rule.tax_rule import get_party_details, get_tax_template
args = {
party_type.lower(): party,
"company": company
@@ -446,6 +464,10 @@
return template
def validate_party_frozen_disabled(party_type, party_name):
+
+ if frappe.flags.ignore_party_validation:
+ return
+
if party_type and party_name:
if party_type in ("Customer", "Supplier"):
party = frappe.get_cached_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
@@ -643,7 +665,7 @@
if out:
try:
return out[0][0]
- except:
+ except Exception:
return None
else:
return None
diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html
index e588ed6..4ac657d 100644
--- a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html
+++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html
@@ -73,4 +73,4 @@
</tr>
</table>
<div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json
index e3afaec..9e126ba 100644
--- a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json
+++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json
@@ -16,7 +16,7 @@
"name": "Bank and Cash Payment Voucher",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json b/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json
index 8c9c266..08b8ef8 100755
--- a/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json
+++ b/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json
@@ -10,6 +10,6 @@
"modified_by": "Administrator",
"name": "Cheque Printing Format",
"owner": "Administrator",
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/credit_note/credit_note.json b/erpnext/accounts/print_format/credit_note/credit_note.json
index c5a11cf..d12b872 100644
--- a/erpnext/accounts/print_format/credit_note/credit_note.json
+++ b/erpnext/accounts/print_format/credit_note/credit_note.json
@@ -17,7 +17,7 @@
"owner": "Administrator",
"parentfield": "__print_formats",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
deleted file mode 100644
index 71c26e8..0000000
--- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
+++ /dev/null
@@ -1,162 +0,0 @@
-{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%}
-{%- set einvoice = json.loads(doc.signed_einvoice) -%}
-
-<div class="page-break">
- <div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
- {% if letter_head and not no_letterhead %}
- <div class="letter-head">{{ letter_head }}</div>
- {% endif %}
- <div class="print-heading">
- <h2>E Invoice<br><small>{{ doc.name }}</small></h2>
- </div>
- </div>
- {% if print_settings.repeat_header_footer %}
- <div id="footer-html" class="visible-pdf">
- {% if not no_letterhead and footer %}
- <div class="letter-head-footer">
- {{ footer }}
- </div>
- {% endif %}
- <p class="text-center small page-number visible-pdf">
- {{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
- </p>
- </div>
- {% endif %}
- <h5 class="font-bold" style="margin-top: 0px;">1. Transaction Details</h5>
- <div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;">
- <div class="col-xs-8 column-break">
- <div class="row data-field">
- <div class="col-xs-4"><label>IRN</label></div>
- <div class="col-xs-8 value">{{ einvoice.Irn }}</div>
- </div>
- <div class="row data-field">
- <div class="col-xs-4"><label>Ack. No</label></div>
- <div class="col-xs-8 value">{{ einvoice.AckNo }}</div>
- </div>
- <div class="row data-field">
- <div class="col-xs-4"><label>Ack. Date</label></div>
- <div class="col-xs-8 value">{{ frappe.utils.format_datetime(einvoice.AckDt, "dd/MM/yyyy hh:mm:ss") }}</div>
- </div>
- <div class="row data-field">
- <div class="col-xs-4"><label>Category</label></div>
- <div class="col-xs-8 value">{{ einvoice.TranDtls.SupTyp }}</div>
- </div>
- <div class="row data-field">
- <div class="col-xs-4"><label>Document Type</label></div>
- <div class="col-xs-8 value">{{ einvoice.DocDtls.Typ }}</div>
- </div>
- <div class="row data-field">
- <div class="col-xs-4"><label>Document No</label></div>
- <div class="col-xs-8 value">{{ einvoice.DocDtls.No }}</div>
- </div>
- </div>
- <div class="col-xs-4 column-break">
- <img src="{{ doc.qrcode_image }}" width="175px" style="float: right;">
- </div>
- </div>
- <h5 class="font-bold" style="margin-top: 15px; margin-bottom: 10px;">2. Party Details</h5>
- <div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;">
- {%- set seller = einvoice.SellerDtls -%}
- <div class="col-xs-6 column-break">
- <h5 style="margin-bottom: 5px;">Seller</h5>
- <p>{{ seller.Gstin }}</p>
- <p>{{ seller.LglNm }}</p>
- <p>{{ seller.Addr1 }}</p>
- {%- if seller.Addr2 -%} <p>{{ seller.Addr2 }}</p> {% endif %}
- <p>{{ seller.Loc }}</p>
- <p>{{ frappe.db.get_value("Address", doc.company_address, "gst_state") }} - {{ seller.Pin }}</p>
-
- {%- if einvoice.ShipDtls -%}
- {%- set shipping = einvoice.ShipDtls -%}
- <h5 style="margin-bottom: 5px;">Shipping</h5>
- <p>{{ shipping.Gstin }}</p>
- <p>{{ shipping.LglNm }}</p>
- <p>{{ shipping.Addr1 }}</p>
- {%- if shipping.Addr2 -%} <p>{{ shipping.Addr2 }}</p> {% endif %}
- <p>{{ shipping.Loc }}</p>
- <p>{{ frappe.db.get_value("Address", doc.shipping_address_name, "gst_state") }} - {{ shipping.Pin }}</p>
- {% endif %}
- </div>
- {%- set buyer = einvoice.BuyerDtls -%}
- <div class="col-xs-6 column-break">
- <h5 style="margin-bottom: 5px;">Buyer</h5>
- <p>{{ buyer.Gstin }}</p>
- <p>{{ buyer.LglNm }}</p>
- <p>{{ buyer.Addr1 }}</p>
- {%- if buyer.Addr2 -%} <p>{{ buyer.Addr2 }}</p> {% endif %}
- <p>{{ buyer.Loc }}</p>
- <p>{{ frappe.db.get_value("Address", doc.customer_address, "gst_state") }} - {{ buyer.Pin }}</p>
- </div>
- </div>
- <div style="overflow-x: auto;">
- <h5 class="font-bold" style="margin-top: 15px; margin-bottom: 10px;">3. Item Details</h5>
- <table class="table table-bordered">
- <thead>
- <tr>
- <th class="text-left" style="width: 3%;">Sr. No.</th>
- <th class="text-left">Item</th>
- <th class="text-left" style="width: 10%;">HSN Code</th>
- <th class="text-left" style="width: 5%;">Qty</th>
- <th class="text-left" style="width: 5%;">UOM</th>
- <th class="text-left">Rate</th>
- <th class="text-left" style="width: 5%;">Discount</th>
- <th class="text-left">Taxable Amount</th>
- <th class="text-left" style="width: 7%;">Tax Rate</th>
- <th class="text-left" style="width: 5%;">Other Charges</th>
- <th class="text-left">Total</th>
- </tr>
- </thead>
- <tbody>
- {% for item in einvoice.ItemList %}
- <tr>
- <td class="text-left" style="width: 3%;">{{ item.SlNo }}</td>
- <td class="text-left">{{ item.PrdDesc }}</td>
- <td class="text-left" style="width: 10%;">{{ item.HsnCd }}</td>
- <td class="text-right" style="width: 5%;">{{ item.Qty }}</td>
- <td class="text-left" style="width: 5%;">{{ item.Unit }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(item.UnitPrice, None, "INR") }}</td>
- <td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(item.Discount, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(item.AssAmt, None, "INR") }}</td>
- <td class="text-right" style="width: 7%;">{{ item.GstRt + item.CesRt }} %</td>
- <td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(item.TotItemVal, None, "INR") }}</td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- </div>
- <div style="overflow-x: auto;">
- <h5 class="font-bold" style="margin-bottom: 0px;">4. Value Details</h5>
- <table class="table table-bordered">
- <thead>
- <tr>
- <th class="text-left">Taxable Amount</th>
- <th class="text-left">CGST</th>
- <th class="text-left"">SGST</th>
- <th class="text-left">IGST</th>
- <th class="text-left">CESS</th>
- <th class="text-left" style="width: 10%;">State CESS</th>
- <th class="text-left">Discount</th>
- <th class="text-left" style="width: 10%;">Other Charges</th>
- <th class="text-left" style="width: 10%;">Round Off</th>
- <th class="text-left">Total Value</th>
- </tr>
- </thead>
- <tbody>
- {%- set value_details = einvoice.ValDtls -%}
- <tr>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.AssVal, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.CgstVal, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.SgstVal, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.IgstVal, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td>
- <td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td>
- </tr>
- </tbody>
- </table>
- </div>
-</div>
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json
deleted file mode 100644
index 1001199..0000000
--- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "align_labels_right": 1,
- "creation": "2020-10-10 18:01:21.032914",
- "custom_format": 0,
- "default_print_language": "en-US",
- "disabled": 1,
- "doc_type": "Sales Invoice",
- "docstatus": 0,
- "doctype": "Print Format",
- "font": "Default",
- "html": "",
- "idx": 0,
- "line_breaks": 1,
- "modified": "2020-10-23 19:54:40.634936",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "GST E-Invoice",
- "owner": "Administrator",
- "print_format_builder": 0,
- "print_format_type": "Jinja",
- "raw_printing": 0,
- "show_section_headings": 1,
- "standard": "Yes"
-}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json b/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json
index 6d7c3d3..1467f27 100644
--- a/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json
+++ b/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json
@@ -16,7 +16,7 @@
"name": "GST Purchase Invoice",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html
index 0ca940f8..c1c611e 100644
--- a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html
+++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html
@@ -68,4 +68,4 @@
</tr>
</table>
<div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json
index 927e818..e45295d 100644
--- a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json
+++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json
@@ -16,7 +16,7 @@
"name": "Journal Auditing Voucher",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html
index 283d505..ae07582 100644
--- a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html
+++ b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html
@@ -27,4 +27,3 @@
{{ _("Authorized Signatory") }}
</p>
</div>
-
diff --git a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json
index f1de448..c52eeb2 100755
--- a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json
+++ b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json
@@ -12,6 +12,6 @@
"name": "Payment Receipt Voucher",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html
index 043ac25..8696bff 100644
--- a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html
+++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html
@@ -103,4 +103,4 @@
</tr>
</table>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json
index 73779d4..3398117 100644
--- a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json
+++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json
@@ -16,7 +16,7 @@
"name": "Purchase Auditing Voucher",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html
index a53b593..efb2d00 100644
--- a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html
+++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html
@@ -93,4 +93,4 @@
</tr>
</table>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json
index 0544e0b..289e184 100644
--- a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json
+++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json
@@ -16,7 +16,7 @@
"name": "Sales Auditing Voucher",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/accounts/report/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py
index be64c32..9ae61dd 100644
--- a/erpnext/accounts/report/account_balance/account_balance.py
+++ b/erpnext/accounts/report/account_balance/account_balance.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
from erpnext.accounts.utils import get_balance_on
+
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns(filters)
diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py
index 14ddf4a..94c73f5 100644
--- a/erpnext/accounts/report/account_balance/test_account_balance.py
+++ b/erpnext/accounts/report/account_balance/test_account_balance.py
@@ -1,10 +1,13 @@
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import getdate
-from erpnext.accounts.report.account_balance.account_balance import execute
+
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.report.account_balance.account_balance import execute
+
class TestAccountBalance(unittest.TestCase):
def test_account_balance(self):
@@ -62,8 +65,3 @@
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 6abd6e5..81c60bb 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -4,7 +4,7 @@
frappe.query_reports["Accounts Payable"] = {
"filters": [
{
- "fieldname":"company",
+ "fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
@@ -12,19 +12,19 @@
"default": frappe.defaults.get_user_default("Company")
},
{
- "fieldname":"report_date",
+ "fieldname": "report_date",
"label": __("Posting Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today()
},
{
- "fieldname":"finance_book",
+ "fieldname": "finance_book",
"label": __("Finance Book"),
"fieldtype": "Link",
"options": "Finance Book"
},
{
- "fieldname":"cost_center",
+ "fieldname": "cost_center",
"label": __("Cost Center"),
"fieldtype": "Link",
"options": "Cost Center",
@@ -38,7 +38,7 @@
}
},
{
- "fieldname":"supplier",
+ "fieldname": "supplier",
"label": __("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
@@ -54,48 +54,48 @@
}
},
{
- "fieldname":"ageing_based_on",
+ "fieldname": "ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date\nSupplier Invoice Date',
"default": "Due Date"
},
{
- "fieldname":"range1",
+ "fieldname": "range1",
"label": __("Ageing Range 1"),
"fieldtype": "Int",
"default": "30",
"reqd": 1
},
{
- "fieldname":"range2",
+ "fieldname": "range2",
"label": __("Ageing Range 2"),
"fieldtype": "Int",
"default": "60",
"reqd": 1
},
{
- "fieldname":"range3",
+ "fieldname": "range3",
"label": __("Ageing Range 3"),
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
- "fieldname":"range4",
+ "fieldname": "range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{
- "fieldname":"payment_terms_template",
+ "fieldname": "payment_terms_template",
"label": __("Payment Terms Template"),
"fieldtype": "Link",
"options": "Payment Terms Template"
},
{
- "fieldname":"supplier_group",
+ "fieldname": "supplier_group",
"label": __("Supplier Group"),
"fieldtype": "Link",
"options": "Supplier Group"
@@ -106,12 +106,17 @@
"fieldtype": "Check"
},
{
- "fieldname":"based_on_payment_terms",
+ "fieldname": "based_on_payment_terms",
"label": __("Based On Payment Terms"),
"fieldtype": "Check",
},
{
- "fieldname":"tax_id",
+ "fieldname": "show_remarks",
+ "label": __("Show Remarks"),
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "tax_id",
"label": __("Tax Id"),
"fieldtype": "Data",
"hidden": 1
@@ -136,4 +141,3 @@
}
erpnext.utils.add_dimensions('Accounts Payable', 9);
-
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.py b/erpnext/accounts/report/accounts_payable/accounts_payable.py
index 246ead6..0de573e 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.py
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.py
@@ -2,9 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
+
def execute(filters=None):
args = {
"party_type": "Supplier",
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
index 9c6b063..ea20072 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
@@ -105,4 +105,3 @@
}
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
-
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py
index 729eda9..6034ec0 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary \
- import AccountsReceivableSummary
+
+from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import (
+ AccountsReceivableSummary,
+)
+
def execute(filters=None):
args = {
@@ -12,4 +14,3 @@
"naming_by": ["Buying Settings", "supp_master_name"],
}
return AccountsReceivableSummary(filters).run(args)
-
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 29c4f7d..5700298 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -4,7 +4,7 @@
frappe.query_reports["Accounts Receivable"] = {
"filters": [
{
- "fieldname":"company",
+ "fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
@@ -12,19 +12,19 @@
"default": frappe.defaults.get_user_default("Company")
},
{
- "fieldname":"report_date",
+ "fieldname": "report_date",
"label": __("Posting Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today()
},
{
- "fieldname":"finance_book",
+ "fieldname": "finance_book",
"label": __("Finance Book"),
"fieldtype": "Link",
"options": "Finance Book"
},
{
- "fieldname":"cost_center",
+ "fieldname": "cost_center",
"label": __("Cost Center"),
"fieldtype": "Link",
"options": "Cost Center",
@@ -38,7 +38,7 @@
}
},
{
- "fieldname":"customer",
+ "fieldname": "customer",
"label": __("Customer"),
"fieldtype": "Link",
"options": "Customer",
@@ -67,66 +67,66 @@
}
},
{
- "fieldname":"ageing_based_on",
+ "fieldname": "ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date',
"default": "Due Date"
},
{
- "fieldname":"range1",
+ "fieldname": "range1",
"label": __("Ageing Range 1"),
"fieldtype": "Int",
"default": "30",
"reqd": 1
},
{
- "fieldname":"range2",
+ "fieldname": "range2",
"label": __("Ageing Range 2"),
"fieldtype": "Int",
"default": "60",
"reqd": 1
},
{
- "fieldname":"range3",
+ "fieldname": "range3",
"label": __("Ageing Range 3"),
"fieldtype": "Int",
"default": "90",
"reqd": 1
},
{
- "fieldname":"range4",
+ "fieldname": "range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{
- "fieldname":"customer_group",
+ "fieldname": "customer_group",
"label": __("Customer Group"),
"fieldtype": "Link",
"options": "Customer Group"
},
{
- "fieldname":"payment_terms_template",
+ "fieldname": "payment_terms_template",
"label": __("Payment Terms Template"),
"fieldtype": "Link",
"options": "Payment Terms Template"
},
{
- "fieldname":"sales_partner",
+ "fieldname": "sales_partner",
"label": __("Sales Partner"),
"fieldtype": "Link",
"options": "Sales Partner"
},
{
- "fieldname":"sales_person",
+ "fieldname": "sales_person",
"label": __("Sales Person"),
"fieldtype": "Link",
"options": "Sales Person"
},
{
- "fieldname":"territory",
+ "fieldname": "territory",
"label": __("Territory"),
"fieldtype": "Link",
"options": "Territory"
@@ -137,45 +137,50 @@
"fieldtype": "Check"
},
{
- "fieldname":"based_on_payment_terms",
+ "fieldname": "based_on_payment_terms",
"label": __("Based On Payment Terms"),
"fieldtype": "Check",
},
{
- "fieldname":"show_future_payments",
+ "fieldname": "show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
},
{
- "fieldname":"show_delivery_notes",
+ "fieldname": "show_delivery_notes",
"label": __("Show Linked Delivery Notes"),
"fieldtype": "Check",
},
{
- "fieldname":"show_sales_person",
+ "fieldname": "show_sales_person",
"label": __("Show Sales Person"),
"fieldtype": "Check",
},
{
- "fieldname":"tax_id",
+ "fieldname": "show_remarks",
+ "label": __("Show Remarks"),
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "tax_id",
"label": __("Tax Id"),
"fieldtype": "Data",
"hidden": 1
},
{
- "fieldname":"customer_name",
+ "fieldname": "customer_name",
"label": __("Customer Name"),
"fieldtype": "Data",
"hidden": 1
},
{
- "fieldname":"payment_terms",
+ "fieldname": "payment_terms",
"label": __("Payment Tems"),
"fieldtype": "Data",
"hidden": 1
},
{
- "fieldname":"credit_limit",
+ "fieldname": "credit_limit",
"label": __("Credit Limit"),
"fieldtype": "Currency",
"hidden": 1
@@ -200,4 +205,3 @@
}
erpnext.utils.add_dimensions('Accounts Receivable', 9);
-
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index b54646f..7f8eade 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -2,12 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe import _, scrub
-from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds
+
from collections import OrderedDict
+
+import frappe
+from frappe import _, scrub
+from frappe.utils import cint, cstr, flt, getdate, nowdate
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
from erpnext.accounts.utils import get_currency_precision
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
# This report gives a summary of all Outstanding Invoices considering the following
@@ -100,6 +106,7 @@
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,
@@ -535,6 +542,8 @@
if getdate(entry_date) > getdate(self.filters.report_date):
row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
+ row.total_due = row.range1 + row.range2 + row.range3 + row.range4 + row.range5
+
def get_ageing_data(self, entry_date, row):
# [0-30, 30-60, 60-90, 90-120, 120-above]
row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
@@ -575,10 +584,12 @@
else:
select_fields = "debit, credit"
+ 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}
+ against_voucher_type, against_voucher, account_currency, {0} {remarks}
from
`tabGL Entry`
where
@@ -587,7 +598,7 @@
and party_type=%s
and (party is not null and party != '')
{1} {2} {3}"""
- .format(select_fields, date_condition, conditions, order_by), values, as_dict=True)
+ .format(select_fields, date_condition, conditions, order_by, remarks=remarks), values, as_dict=True)
def get_sales_invoices_or_customers_based_on_sales_person(self):
if self.filters.get("sales_person"):
@@ -746,6 +757,10 @@
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')
if self.party_type == "Supplier":
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index 2ff5b53..1d24561 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,11 +1,14 @@
from __future__ import unicode_literals
-import frappe
-import frappe.defaults
+
import unittest
-from frappe.utils import today, getdate, add_days
-from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
+import frappe
+from frappe.utils import add_days, getdate, today
+
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
+
class TestAccountsReceivable(unittest.TestCase):
def test_accounts_receivable(self):
@@ -93,4 +96,3 @@
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 657b3e8..106f224 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, scrub
-from frappe.utils import flt, cint
+from frappe.utils import cint
+from six import iteritems
+
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
-from six import iteritems
+
def execute(filters=None):
args = {
@@ -82,6 +85,7 @@
"range3": 0.0,
"range4": 0.0,
"range5": 0.0,
+ "total_due": 0.0,
"sales_person": []
}))
@@ -134,4 +138,7 @@
"{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))
\ No newline at end of file
+ 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')
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 2162a02..0fd4ca0 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import flt
+
+import frappe
from frappe import _
+from frappe.utils import flt
+
def execute(filters=None):
columns, data = get_columns(), get_data(filters)
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 5001ad9..d34bc85 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
@@ -2,9 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import formatdate, flt, add_days
+from frappe.utils import add_days, flt, formatdate
def execute(filters=None):
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py
index 26bb44f..78ee7ca 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -2,11 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, cint
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
- get_filtered_list_for_consolidated_report)
+from frappe.utils import cint, flt
+
+from erpnext.accounts.report.financial_statements import (
+ get_columns,
+ get_data,
+ get_filtered_list_for_consolidated_report,
+ get_period_list,
+)
+
def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@@ -209,4 +216,4 @@
else:
chart["type"] = "line"
- return chart
\ No newline at end of file
+ return chart
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
index dbee022..f0b6c6b 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
@@ -22,7 +22,7 @@
"fieldtype": "Link",
"options": "Account",
"reqd": 1,
- "default": frappe.defaults.get_user_default("Company")?
+ "default": frappe.defaults.get_user_default("Company")?
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "",
"get_query": function() {
return {
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 79b0a6f..1a1fa96 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import nowdate, getdate
+from frappe.utils import getdate, nowdate
+
def execute(filters=None):
if not filters: filters = {}
@@ -74,19 +76,19 @@
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
+ FROM
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
- WHERE
+ 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)
payment_entries = frappe.db.sql("""SELECT
- "Payment Entry", name, posting_date, reference_no, clearance_date, party,
+ "Payment Entry", name, posting_date, reference_no, clearance_date, party,
if(paid_from=%(account)s, paid_amount * -1, received_amount)
- FROM
+ FROM
`tabPayment Entry`
- WHERE
+ 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)
- return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate()))
\ No newline at end of file
+ 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.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
index 8f02849..9bb6a14 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
@@ -16,7 +16,7 @@
"label": __("Bank Account"),
"fieldtype": "Link",
"options": "Account",
- "default": frappe.defaults.get_user_default("Company")?
+ "default": frappe.defaults.get_user_default("Company")?
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "",
"reqd": 1,
"get_query": function() {
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 63317c5..b984306 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt, getdate, nowdate
from frappe import _
+from frappe.utils import flt, getdate, nowdate
+
def execute(filters=None):
if not filters: filters = {}
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 2ce5d50..6c4cd67 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
data = get_data(filters) or []
columns = get_columns()
@@ -104,4 +106,4 @@
'fieldtype': 'Currency',
'width': 100
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
index f547ca6..718b6e2 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
@@ -92,4 +92,3 @@
erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
});
-
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 f1b231b..c204250 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -2,12 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import datetime
-from six import iteritems
import frappe
from frappe import _
from frappe.utils import flt, formatdate
+from six import iteritems
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
@@ -38,8 +39,8 @@
GROUP BY parent''',{'dimension':[dimension]})
if DCC_allocation:
filters['budget_against_filter'] = [DCC_allocation[0][0]]
- cam_map = get_dimension_account_month_map(filters)
- dimension_items = cam_map.get(DCC_allocation[0][0])
+ ddc_cam_map = get_dimension_account_month_map(filters)
+ dimension_items = ddc_cam_map.get(DCC_allocation[0][0])
if dimension_items:
data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1])
@@ -48,7 +49,6 @@
return columns, data, None, chart
def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
-
for account, monthwise_data in iteritems(dimension_items):
row = [dimension, account]
totals = [0, 0, 0]
@@ -79,7 +79,7 @@
if filters["period"] != "Yearly" :
row += totals
data.append(row)
-
+
return data
@@ -389,7 +389,7 @@
budget_values[i] += values[index]
actual_values[i] += values[index+1]
index += 3
-
+
return {
'data': {
'labels': labels,
@@ -400,4 +400,3 @@
},
'type' : 'bar'
}
-
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.html b/erpnext/accounts/report/cash_flow/cash_flow.html
index 40ba20c..d4ae54d 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.html
+++ b/erpnext/accounts/report/cash_flow/cash_flow.html
@@ -1 +1 @@
-{% include "accounts/report/financial_statements.html" %}
\ No newline at end of file
+{% include "accounts/report/financial_statements.html" %}
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js
index a984bf4..a2c34c6 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.js
+++ b/erpnext/accounts/report/cash_flow/cash_flow.js
@@ -21,4 +21,4 @@
"default": 1
}
);
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index 3577457..bb8138b 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -2,14 +2,23 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import cint, cstr
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, get_filtered_list_for_consolidated_report)
-from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
-from erpnext.accounts.utils import get_fiscal_year
from six import iteritems
+from erpnext.accounts.report.financial_statements import (
+ get_columns,
+ get_data,
+ get_filtered_list_for_consolidated_report,
+ get_period_list,
+)
+from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
+ get_net_profit_loss,
+)
+from erpnext.accounts.utils import get_fiscal_year
+
def execute(filters=None):
if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
@@ -130,9 +139,9 @@
data["total"] = total
return data
-def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters={}):
+def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None):
cond = ""
- filters = frappe._dict(filters)
+ filters = frappe._dict(filters or {})
if filters.include_default_book_entries:
company_fb = frappe.db.get_value("Company", company, 'default_finance_book')
diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
index c11c153..bbc020e 100644
--- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
@@ -2,11 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import add_to_date
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
-from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
+
+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 (
+ get_net_profit_loss,
+)
def get_mapper_for(mappers, position):
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 1363b53..e24a5f9 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -94,17 +94,20 @@
"default": 1
}
],
- "formatter": function(value, row, column, data, default_formatter) {
+ "formatter": function(value, row, column, data, default_formatter) {
if (data && column.fieldname=="account") {
value = data.account_name || value;
-
+
column.link_onclick =
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
column.is_tree = true;
}
- value = default_formatter(value, row, column, data);
+ if (data && data.account && column.apply_currency_formatter) {
+ data.currency = erpnext.get_currency(column.company_name);
+ }
+ value = default_formatter(value, row, column, data);
if (!data.parent_account) {
value = $(`<span>${value}</span>`);
@@ -126,4 +129,4 @@
});
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index 56a67bb..a600ead 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -2,17 +2,39 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+from collections import defaultdict
+
+import frappe
from frappe import _
-from frappe.utils import flt, cint, getdate
-from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
+from frappe.utils import cint, flt, getdate
+
+import erpnext
+from erpnext.accounts.report.balance_sheet.balance_sheet import (
+ get_chart_data,
+ get_provisional_profit_loss,
+)
+from erpnext.accounts.report.balance_sheet.balance_sheet import (
+ get_report_summary as get_bs_summary,
+)
+from erpnext.accounts.report.cash_flow.cash_flow import (
+ add_total_row_account,
+ get_account_type_based_gl_data,
+ get_cash_flow_accounts,
+)
+from erpnext.accounts.report.cash_flow.cash_flow import get_report_summary as get_cash_flow_summary
from erpnext.accounts.report.financial_statements import get_fiscal_year_data, sort_accounts
-from erpnext.accounts.report.balance_sheet.balance_sheet import (get_provisional_profit_loss,
- check_opening_balance, get_chart_data, get_report_summary as get_bs_summary)
-from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (get_net_profit_loss,
- get_chart_data as get_pl_chart_data, get_report_summary as get_pl_summary)
-from erpnext.accounts.report.cash_flow.cash_flow import (get_cash_flow_accounts, get_account_type_based_gl_data,
- add_total_row_account, get_report_summary as get_cash_flow_summary)
+from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
+ get_chart_data as get_pl_chart_data,
+)
+from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
+ get_net_profit_loss,
+)
+from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
+ get_report_summary as get_pl_summary,
+)
+from erpnext.accounts.report.utils import convert, convert_to_presentation_currency
+
def execute(filters=None):
columns, data, message, chart = [], [], [], []
@@ -22,7 +44,7 @@
fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year'))
companies_column, companies = get_companies(filters)
- columns = get_columns(companies_column)
+ columns = get_columns(companies_column, filters)
if filters.get('report') == "Balance Sheet":
data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
@@ -53,21 +75,24 @@
provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
companies, filters.get('company'), company_currency, True)
- message, opening_balance = check_opening_balance(asset, liability, equity)
+ message, opening_balance = prepare_companywise_opening_balance(asset, liability, equity, companies)
- if opening_balance and round(opening_balance,2) !=0:
- unclosed ={
+ if opening_balance:
+ unclosed = {
"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
"warn_if_negative": True,
"currency": company_currency
}
- for company in companies:
- unclosed[company] = opening_balance
- if provisional_profit_loss:
- provisional_profit_loss[company] = provisional_profit_loss[company] - opening_balance
- unclosed["total"]=opening_balance
+ for company in companies:
+ unclosed[company] = opening_balance.get(company)
+ if provisional_profit_loss and provisional_profit_loss.get(company):
+ provisional_profit_loss[company] = (
+ flt(provisional_profit_loss[company]) - flt(opening_balance.get(company))
+ )
+
+ unclosed["total"] = opening_balance.get(company)
data.append(unclosed)
if provisional_profit_loss:
@@ -82,6 +107,37 @@
return data, message, chart, report_summary
+def prepare_companywise_opening_balance(asset_data, liability_data, equity_data, companies):
+ opening_balance = {}
+ for company in companies:
+ opening_value = 0
+
+ # opening_value = Aseet - liability - equity
+ for data in [asset_data, liability_data, equity_data]:
+ account_name = get_root_account_name(data[0].root_type, company)
+ opening_value += get_opening_balance(account_name, data, company)
+
+ opening_balance[company] = opening_value
+
+ if opening_balance:
+ return _("Previous Financial Year is not closed"), opening_balance
+
+ return '', {}
+
+def get_opening_balance(account_name, data, company):
+ for row in data:
+ if row.get('account_name') == account_name:
+ return row.get('company_wise_opening_bal', {}).get(company, 0.0)
+
+def get_root_account_name(root_type, company):
+ return frappe.get_all(
+ 'Account',
+ fields=['account_name'],
+ filters = {'root_type': root_type, 'is_group': 1,
+ 'company': company, 'parent_account': ('is', 'not set')},
+ as_list=1
+ )[0][0]
+
def get_profit_loss_data(fiscal_year, companies, columns, filters):
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
company_currency = get_company_currency(filters)
@@ -173,30 +229,37 @@
data["total"] = total
return data
-def get_columns(companies):
- columns = [{
- "fieldname": "account",
- "label": _("Account"),
- "fieldtype": "Link",
- "options": "Account",
- "width": 300
- }]
-
- columns.append({
- "fieldname": "currency",
- "label": _("Currency"),
- "fieldtype": "Link",
- "options": "Currency",
- "hidden": 1
- })
+def get_columns(companies, filters):
+ columns = [
+ {
+ "fieldname": "account",
+ "label": _("Account"),
+ "fieldtype": "Link",
+ "options": "Account",
+ "width": 300
+ }, {
+ "fieldname": "currency",
+ "label": _("Currency"),
+ "fieldtype": "Link",
+ "options": "Currency",
+ "hidden": 1
+ }
+ ]
for company in companies:
+ apply_currency_formatter = 1 if not filters.presentation_currency else 0
+ currency = filters.presentation_currency
+ if not currency:
+ currency = erpnext.get_company_currency(company)
+
columns.append({
"fieldname": company,
- "label": company,
+ "label": f'{company} ({currency})',
"fieldtype": "Currency",
"options": "currency",
- "width": 150
+ "width": 150,
+ "apply_currency_formatter": apply_currency_formatter,
+ "company_name": company
})
return columns
@@ -210,12 +273,14 @@
company_currency = get_company_currency(filters)
if filters.filter_based_on == 'Fiscal Year':
- start_date = fiscal_year.year_start_date
+ 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
+ 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):
@@ -224,9 +289,10 @@
end_date, root.lft, root.rgt, filters,
gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
- calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters)
+ calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year)
accumulate_values_into_parents(accounts, accounts_by_name, companies)
- out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency)
+
+ out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters)
if out:
add_total_row(out, root_type, balance_must_be, companies, company_currency)
@@ -237,19 +303,44 @@
return (filters.get('presentation_currency')
or frappe.get_cached_value('Company', filters.company, "default_currency"))
-def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters):
+def calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year):
+ start_date = (fiscal_year.year_start_date
+ if filters.filter_based_on == 'Fiscal Year' else filters.period_start_date)
+
for entries in gl_entries_by_account.values():
for entry in entries:
- d = accounts_by_name.get(entry.account_name)
+ if entry.account_number:
+ account_name = entry.account_number + ' - ' + entry.account_name
+ else:
+ account_name = entry.account_name
+
+ d = accounts_by_name.get(account_name)
+
if d:
+ debit, credit = 0, 0
for company in companies:
# check if posting date is within the period
if (entry.company == company or (filters.get('accumulated_in_group_company'))
and entry.company in companies.get(company)):
- d[company] = d.get(company, 0.0) + flt(entry.debit) - flt(entry.credit)
+ parent_company_currency = erpnext.get_company_currency(d.company)
+ child_company_currency = erpnext.get_company_currency(entry.company)
+
+ debit, credit = flt(entry.debit), flt(entry.credit)
+
+ if (not filters.get('presentation_currency')
+ and entry.company != company
+ and parent_company_currency != child_company_currency
+ and filters.get('accumulated_in_group_company')):
+ debit = convert(debit, parent_company_currency, child_company_currency, filters.end_date)
+ credit = convert(credit, parent_company_currency, child_company_currency, filters.end_date)
+
+ d[company] = d.get(company, 0.0) + flt(debit) - flt(credit)
+
+ if entry.posting_date < getdate(start_date):
+ d['company_wise_opening_bal'][company] += (flt(debit) - flt(credit))
if entry.posting_date < getdate(start_date):
- d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
+ d["opening_balance"] = d.get("opening_balance", 0.0) + flt(debit) - flt(credit)
def accumulate_values_into_parents(accounts, accounts_by_name, companies):
"""accumulate children's values in parent accounts"""
@@ -257,17 +348,18 @@
if d.parent_account:
account = d.parent_account_name
- if not accounts_by_name.get(account):
- continue
+ # if not accounts_by_name.get(account):
+ # continue
for company in companies:
accounts_by_name[account][company] = \
accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
+ accounts_by_name[account]['company_wise_opening_bal'][company] += d.get('company_wise_opening_bal', {}).get(company, 0.0)
+
accounts_by_name[account]["opening_balance"] = \
accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
-
def get_account_heads(root_type, companies, filters):
accounts = get_accounts(root_type, filters)
@@ -287,7 +379,14 @@
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 = { d.name : d.account_name for d in accounts }
+ name_to_account_map = {}
+
+ for d in accounts:
+ if d.account_number:
+ account_name = d.account_number + ' - ' + d.account_name
+ else:
+ account_name = d.account_name
+ name_to_account_map[d.name] = account_name
for account in accounts:
if account.parent_account:
@@ -321,7 +420,7 @@
`tabAccount` where company = %s and root_type = %s
""" , (filters.get('company'), root_type), as_dict=1)
-def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency):
+def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters):
data = []
for d in accounts:
@@ -335,10 +434,13 @@
"parent_account": _(d.parent_account),
"indent": flt(d.indent),
"year_start_date": start_date,
+ "root_type": d.root_type,
"year_end_date": end_date,
- "currency": company_currency,
+ "currency": filters.presentation_currency,
+ "company_wise_opening_bal": d.company_wise_opening_bal,
"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
})
+
for company in companies:
if d.get(company) and balance_must_be == "Credit":
# change sign based on Debit or Credit, since calculation is done using (debit - credit)
@@ -353,6 +455,7 @@
row["has_value"] = has_value
row["total"] = total
+
data.append(row)
return data
@@ -400,7 +503,11 @@
convert_to_presentation_currency(gl_entries, currency_info, filters.get('company'))
for entry in gl_entries:
- account_name = entry.account_name
+ if entry.account_number:
+ account_name = entry.account_number + ' - ' + entry.account_name
+ else:
+ account_name = entry.account_name
+
validate_entries(account_name, entry, accounts_by_name, accounts)
gl_entries_by_account.setdefault(account_name, []).append(entry)
@@ -411,6 +518,7 @@
'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
def validate_entries(key, entry, accounts_by_name, accounts):
+ # If an account present in the child company and not in the parent company
if key not in accounts_by_name:
args = get_account_details(entry.account)
@@ -420,12 +528,23 @@
args.update({
'lft': parent_args.lft + 1,
'rgt': parent_args.rgt - 1,
+ 'indent': 3,
'root_type': parent_args.root_type,
- 'report_type': parent_args.report_type
+ 'report_type': parent_args.report_type,
+ 'parent_account_name': parent_args.account_name,
+ 'company_wise_opening_bal': defaultdict(float)
})
accounts_by_name.setdefault(key, args)
- accounts.append(args)
+
+ idx = len(accounts)
+ # To identify parent account index
+ for index, row in enumerate(accounts):
+ if row.parent_account_name == args.parent_account_name:
+ idx = index
+ break
+
+ accounts.insert(idx+1, args)
def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions = []
@@ -455,7 +574,6 @@
for company in companies:
total_row.setdefault(company, 0.0)
total_row[company] += row.get(company, 0.0)
- row[company] = 0.0
total_row.setdefault("total", 0.0)
total_row["total"] += flt(row["total"])
@@ -471,7 +589,13 @@
parent_children_map = {}
accounts_by_name = {}
for d in accounts:
- accounts_by_name[d.account_name] = d
+ if d.account_number:
+ account_name = d.account_number + ' - ' + d.account_name
+ else:
+ account_name = d.account_name
+ d['company_wise_opening_bal'] = defaultdict(float)
+ accounts_by_name[account_name] = d
+
parent_children_map.setdefault(d.parent_account or None, []).append(d)
filtered_accounts = []
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 c79d740..603fbac 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
@@ -2,12 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _, scrub
from frappe.utils import getdate, nowdate
from six import iteritems, itervalues
+
class PartyLedgerSummaryReport(object):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
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 515fd99..f096094 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
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
+
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
+
def execute(filters=None):
columns = get_column()
args = get_args()
@@ -105,4 +107,4 @@
def get_args():
return {'doctype': 'Delivery Note', 'party': 'customer',
- 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}
\ No newline at end of file
+ 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}
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 de7ed49..4212137 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
@@ -2,14 +2,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe import _
-from frappe.utils import (flt, cstr)
-from erpnext.accounts.report.financial_statements import filter_accounts, filter_out_zero_value_rows
+import frappe
+from frappe import _
+from frappe.utils import cstr, flt
+from six import itervalues
+
+import erpnext
+from erpnext.accounts.report.financial_statements import (
+ filter_accounts,
+ filter_out_zero_value_rows,
+)
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
-from six import itervalues
def execute(filters=None):
validate_filters(filters)
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 39ff804..2cb8a68 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -5,19 +5,23 @@
from __future__ import unicode_literals
-import re
-from past.builtins import cmp
import functools
import math
+import re
-import frappe, erpnext
-from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
-from erpnext.accounts.utils import get_fiscal_year
+import frappe
from frappe import _
-from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr, cint)
-
+from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
+from past.builtins import cmp
from six import itervalues
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
+from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
+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):
@@ -339,7 +343,7 @@
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
def compare_accounts(a, b):
- if re.split('\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 cmp(a[key], b[key])
elif is_root:
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 4a551b8..b296876 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -110,9 +110,26 @@
"fieldname":"group_by",
"label": __("Group by"),
"fieldtype": "Select",
- "options": ["", __("Group by Voucher"), __("Group by Voucher (Consolidated)"),
- __("Group by Account"), __("Group by Party")],
- "default": __("Group by Voucher (Consolidated)")
+ "options": [
+ "",
+ {
+ label: __("Group by Voucher"),
+ value: "Group by Voucher",
+ },
+ {
+ label: __("Group by Voucher (Consolidated)"),
+ value: "Group by Voucher (Consolidated)",
+ },
+ {
+ label: __("Group by Account"),
+ value: "Group by Account",
+ },
+ {
+ label: __("Group by Party"),
+ value: "Group by Party",
+ },
+ ],
+ "default": "Group by Voucher (Consolidated)"
},
{
"fieldname":"tax_id",
@@ -176,4 +193,3 @@
}
erpnext.utils.add_dimensions('General Ledger', 15)
-
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 1759fa3..5bd6e58 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -2,17 +2,24 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from erpnext import get_company_currency, get_default_company
-from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
-from frappe.utils import getdate, cstr, flt, fmt_money
-from frappe import _, _dict
-from erpnext.accounts.utils import get_account_currency
-from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
-from six import iteritems
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
+
from collections import OrderedDict
+import frappe
+from frappe import _, _dict
+from frappe.utils import cstr, flt, getdate
+from six import iteritems
+
+from erpnext import get_company_currency, get_default_company
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
+from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
+from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
+from erpnext.accounts.utils import get_account_currency
+
+
def execute(filters=None):
if not filters:
return [], []
@@ -48,21 +55,21 @@
if not filters.get("from_date") and not filters.get("to_date"):
frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
-
+
if filters.get('account'):
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')):
+ 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')]):
+ 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:
@@ -78,13 +85,10 @@
def validate_party(filters):
party_type, party = filters.get("party_type"), filters.get("party")
- if party:
- if not party_type:
- frappe.throw(_("To filter based on Party, select Party Type first"))
- else:
- for d in party:
- if not frappe.db.exists(party_type, d):
- frappe.throw(_("Invalid {0}: {1}").format(party_type, d))
+ if party and party_type:
+ for d in party:
+ 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):
@@ -92,7 +96,7 @@
account_currency = None
if filters.get("account"):
- if len(filters.get("account")) == 1:
+ if len(filters.get("account")) == 1:
account_currency = get_account_currency(filters.account[0])
else:
currency = get_account_currency(filters.account[0])
@@ -149,7 +153,7 @@
if filters.get("include_dimensions"):
order_by_statement = "order by posting_date, creation"
- if filters.get("group_by") == _("Group by Voucher"):
+ if filters.get("group_by") == "Group by Voucher":
order_by_statement = "order by posting_date, voucher_type, voucher_no"
if filters.get("include_default_book_entries"):
@@ -308,13 +312,13 @@
# 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 iteritems(gle_map):
# acc
if acc_dict.entries:
# opening
data.append({})
- if filters.get("group_by") != _("Group by Voucher"):
+ if filters.get("group_by") != "Group by Voucher":
data.append(acc_dict.totals.opening)
data += acc_dict.entries
@@ -323,7 +327,7 @@
data.append(acc_dict.totals.total)
# closing
- if filters.get("group_by") != _("Group by Voucher"):
+ if filters.get("group_by") != "Group by Voucher":
data.append(acc_dict.totals.closing)
data.append({})
else:
@@ -353,9 +357,9 @@
)
def group_by_field(group_by):
- if group_by == _('Group by Party'):
+ if group_by == 'Group by Party':
return 'party'
- elif group_by in [_('Group by Voucher (Consolidated)'), _('Group by Account')]:
+ elif group_by in ['Group by Voucher (Consolidated)', 'Group by Account']:
return 'account'
else:
return 'voucher_no'
@@ -419,9 +423,9 @@
elif gle.posting_date <= to_date:
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'total', gle)
update_value_in_dict(totals, 'total', gle)
- if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
+ if filters.get("group_by") != 'Group by Voucher (Consolidated)':
gle_map[gle.get(group_by)].entries.append(gle)
- elif filters.get("group_by") == _('Group by Voucher (Consolidated)'):
+ elif filters.get("group_by") == 'Group by Voucher (Consolidated)':
keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")]
for dim in accounting_dimensions:
keylist.append(gle.get(dim))
diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html
index 40ba20c..d4ae54d 100644
--- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html
+++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html
@@ -1 +1 @@
-{% include "accounts/report/financial_statements.html" %}
\ No newline at end of file
+{% include "accounts/report/financial_statements.html" %}
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 714e48d..49522d9 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
@@ -2,11 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import copy
+
import frappe
from frappe import _
from frappe.utils import flt
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
-import copy
+
+from erpnext.accounts.report.financial_statements import get_columns, get_data, get_period_list
+
def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.period_start_date,
@@ -165,4 +169,4 @@
has_value=True
if has_value:
- return profit_loss
\ No newline at end of file
+ return profit_loss
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js
index ba17a94..856b97d 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.js
+++ b/erpnext/accounts/report/gross_profit/gross_profit.js
@@ -36,5 +36,20 @@
"options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject",
"default": "Invoice"
},
- ]
+ ],
+ "tree": true,
+ "name_field": "parent",
+ "parent_field": "parent_invoice",
+ "initial_depth": 3,
+ "formatter": function(value, row, column, data, default_formatter) {
+ value = default_formatter(value, row, column, data);
+
+ if (data && data.indent == 0.0) {
+ value = $(`<span>${value}</span>`);
+ var $value = $(value).css("font-weight", "bold");
+ value = $value.wrap("<p></p>").parent().html();
+ }
+
+ return value;
+ },
}
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.json b/erpnext/accounts/report/gross_profit/gross_profit.json
index cd6bac2..5fff3fd 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.json
+++ b/erpnext/accounts/report/gross_profit/gross_profit.json
@@ -1,16 +1,20 @@
{
- "add_total_row": 1,
+ "add_total_row": 0,
+ "columns": [],
"creation": "2013-02-25 17:03:34",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
+ "filters": [],
"idx": 3,
"is_standard": "Yes",
- "modified": "2020-08-13 11:26:39.112352",
+ "modified": "2021-08-19 18:57:07.468202",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Gross Profit",
"owner": "Administrator",
+ "prepared_report": 0,
"ref_doctype": "Sales Invoice",
"report_name": "Gross Profit",
"report_type": "Script Report",
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 6d8623c..f08bca9 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, scrub
-from erpnext.stock.utils import get_incoming_rate
+from frappe.utils import cint, flt
+
from erpnext.controllers.queries import get_match_cond
-from frappe.utils import flt, cint
+from erpnext.stock.utils import get_incoming_rate
def execute(filters=None):
@@ -41,16 +43,44 @@
columns = get_columns(group_wise_columns, filters)
- for src in gross_profit_data.grouped_data:
+ if filters.group_by == 'Invoice':
+ get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data)
+
+ else:
+ get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data)
+
+ return 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'
+ # removing Item Code and Item Name columns
+ del columns[4:6]
+
+ for src in gross_profit_data.si_list:
+ row = frappe._dict()
+ row.indent = src.indent
+ row.parent_invoice = src.parent_invoice
+ row.currency = filters.currency
+
+ for col in group_wise_columns.get(scrub(filters.group_by)):
+ row[column_names[col]] = src.get(col)
+
+ data.append(row)
+
+def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
+ for idx, src in enumerate(gross_profit_data.grouped_data):
row = []
for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col))
row.append(filters.currency)
+ if idx == len(gross_profit_data.grouped_data)-1:
+ row[0] = frappe.bold("Total")
data.append(row)
- return columns, data
-
def get_columns(group_wise_columns, filters):
columns = []
column_map = frappe._dict({
@@ -91,12 +121,38 @@
return columns
+def get_column_names():
+ return frappe._dict({
+ 'parent': '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):
self.data = []
self.average_buying_rate = {}
self.filters = frappe._dict(filters)
self.load_invoice_items()
+
+ if filters.group_by == 'Invoice':
+ self.group_items_by_invoice()
+
self.load_stock_ledger_entries()
self.load_product_bundle()
self.load_non_stock_items()
@@ -110,7 +166,12 @@
self.currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
self.float_precision = cint(frappe.db.get_default("float_precision")) or 2
- for row in self.si_list:
+ grouped_by_invoice = True if self.filters.get("group_by") == "Invoice" else False
+
+ if grouped_by_invoice:
+ buying_amount = 0
+
+ for row in reversed(self.si_list):
if self.skip_row(row, self.product_bundles):
continue
@@ -132,12 +193,20 @@
row.buying_amount = flt(self.get_buying_amount(row, row.item_code),
self.currency_precision)
+ if grouped_by_invoice:
+ if row.indent == 1.0:
+ buying_amount += row.buying_amount
+ elif row.indent == 0.0:
+ row.buying_amount = buying_amount
+ buying_amount = 0
+
# get buying rate
- if row.qty:
- row.buying_rate = flt(row.buying_amount / row.qty, self.float_precision)
- row.base_rate = flt(row.base_amount / row.qty, self.float_precision)
+ if flt(row.qty):
+ row.buying_rate = flt(row.buying_amount / flt(row.qty), self.float_precision)
+ row.base_rate = flt(row.base_amount / flt(row.qty), self.float_precision)
else:
- row.buying_rate, row.base_rate = 0.0, 0.0
+ if self.is_not_invoice_row(row):
+ row.buying_rate, row.base_rate = 0.0, 0.0
# calculate gross profit
row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
@@ -154,38 +223,61 @@
def get_average_rate_based_on_group_by(self):
# sum buying / selling totals for group
+ self.totals = frappe._dict(
+ qty=0,
+ base_amount=0,
+ buying_amount=0,
+ gross_profit=0,
+ gross_profit_percent=0,
+ base_rate=0,
+ buying_rate=0
+ )
for key in list(self.grouped):
if self.filters.get("group_by") != "Invoice":
for i, row in enumerate(self.grouped[key]):
if i==0:
new_row = row
else:
- new_row.qty += row.qty
+ new_row.qty += flt(row.qty)
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
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)
+ self.add_to_totals(new_row)
else:
for i, row in enumerate(self.grouped[key]):
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 += returned_item_row.qty
+ row.qty += flt(returned_item_row.qty)
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
- row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
- if row.qty or row.base_amount:
+ row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
+ if (flt(row.qty) or row.base_amount) and self.is_not_invoice_row(row):
row = self.set_average_rate(row)
self.grouped_data.append(row)
+ self.add_to_totals(row)
+ self.set_average_gross_profit(self.totals)
+ self.grouped_data.append(self.totals)
+
+ 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"
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
+ 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.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 add_to_totals(self, new_row):
+ for key in self.totals:
+ if new_row.get(key):
+ self.totals[key] += new_row[key]
def get_returned_invoice_items(self):
returned_invoices = frappe.db.sql("""
@@ -332,6 +424,109 @@
.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)
+ def group_items_by_invoice(self):
+ """
+ Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children.
+ """
+
+ parents = []
+
+ for row in self.si_list:
+ if row.parent not in parents:
+ parents.append(row.parent)
+
+ parents_index = 0
+ for index, row in enumerate(self.si_list):
+ if parents_index < len(parents) and row.parent == parents[parents_index]:
+ invoice = self.get_invoice_row(row)
+ self.si_list.insert(index, invoice)
+ parents_index += 1
+
+ else:
+ # skipping the bundle items rows
+ if not row.indent:
+ row.indent = 1.0
+ row.parent_invoice = row.parent
+ row.parent = 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,
+ 'parent': row.parent,
+ '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)
+
+ def get_bundle_items(self, product_bundle):
+ return frappe.get_all(
+ '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': 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']
+ )
+
def load_stock_ledger_entries(self):
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
voucher_detail_no, stock_value, warehouse, actual_qty as qty
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 7dea80c..8f82271 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import getdate, add_days, today, cint
from frappe import _
+from frappe.utils import cint
+
def execute(filters=None):
columns = get_columns()
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 685419a..cd25c05 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
@@ -2,14 +2,23 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
from frappe.utils import flt
-from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts,
- get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row,
- get_group_by_conditions)
+
+import erpnext
+from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (
+ add_sub_total_row,
+ add_total_row,
+ get_grand_total,
+ get_group_by_and_display_fields,
+ get_group_by_conditions,
+ get_tax_accounts,
+)
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details
+
def execute(filters=None):
return _execute(filters)
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 2e794da..847a127 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
@@ -2,13 +2,19 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import flt, cstr
from frappe.model.meta import get_field_precision
+from frappe.utils import cstr, flt
from frappe.utils.xlsxutils import handle_html
+
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
-from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details
+from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import (
+ get_customer_details,
+ get_item_details,
+)
+
def execute(filters=None):
return _execute(filters)
@@ -76,7 +82,7 @@
'company': d.company,
'sales_order': d.sales_order,
'delivery_note': d.delivery_note,
- 'income_account': d.unrealized_profit_loss_account or d.income_account,
+ '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
@@ -380,6 +386,7 @@
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
`tabSales Invoice`.unrealized_profit_loss_account,
+ `tabSales Invoice`.is_internal_customer,
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
@@ -625,7 +632,3 @@
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')])
-
-
-
-
diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py
index 2e18ce1..b61f87d 100644
--- a/erpnext/accounts/report/non_billed_report.py
+++ b/erpnext/accounts/report/non_billed_report.py
@@ -2,11 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _
-from erpnext import get_default_currency
from frappe.model.meta import get_field_precision
+from erpnext import get_default_currency
+
+
def get_ordered_to_be_billed_data(args):
doctype, party = args.get('doctype'), args.get('party')
child_tab = doctype + " Item"
@@ -44,4 +46,4 @@
def get_project_field(doctype, party):
if party == "supplier": doctype = doctype + ' Item'
- return "`tab%s`.project"%(doctype)
\ No newline at end of file
+ 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 7195c7e..6c6af1c 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
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+from frappe.utils import flt, getdate
+
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
-from frappe.utils import getdate, flt
def execute(filters=None):
@@ -40,7 +42,7 @@
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,
+ invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks,
d.age, d.range1, d.range2, d.range3, d.range4
]
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index 6a42bb4..c9463ca 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -2,15 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _, _dict
-from erpnext import get_company_currency, get_default_company
+from frappe import _
+
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
+
def execute(filters=None):
if not filters:
return [], []
-
+
validate_filters(filters)
columns = get_columns(filters)
@@ -29,7 +31,7 @@
invoice_map, grouped_data = {}, []
for d in pos_entries:
invoice_map.setdefault(d[group_by_field], []).append(d)
-
+
for key in invoice_map:
invoices = invoice_map[key]
grouped_data += invoices
@@ -56,7 +58,7 @@
return frappe.db.sql(
"""
- SELECT
+ SELECT
p.posting_date, p.name as pos_invoice, p.pos_profile,
p.owner, p.base_grand_total as grand_total, p.base_paid_amount as paid_amount,
p.customer, p.is_return {select_mop_field}
@@ -96,22 +98,22 @@
def validate_filters(filters):
if not filters.get("company"):
frappe.throw(_("{0} is mandatory").format(_("Company")))
-
+
if not filters.get("from_date") and not filters.get("to_date"):
frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
-
+
if filters.from_date > filters.to_date:
frappe.throw(_("From Date must be before To Date"))
if (filters.get("pos_profile") and filters.get("group_by") == _('POS Profile')):
frappe.throw(_("Can not filter based on POS Profile, if grouped by POS Profile"))
-
+
if (filters.get("customer") and filters.get("group_by") == _('Customer')):
frappe.throw(_("Can not filter based on Customer, if grouped by Customer"))
-
+
if (filters.get("owner") and filters.get("group_by") == _('Cashier')):
frappe.throw(_("Can not filter based on Cashier, if grouped by Cashier"))
-
+
if (filters.get("mode_of_payment") and filters.get("group_by") == _('Payment Method')):
frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
@@ -120,23 +122,23 @@
if filters.get("pos_profile"):
conditions += " AND pos_profile = %(pos_profile)s"
-
+
if filters.get("owner"):
conditions += " AND owner = %(owner)s"
-
+
if filters.get("customer"):
conditions += " AND customer = %(customer)s"
-
+
if filters.get("is_return"):
conditions += " AND is_return = %(is_return)s"
-
+
if filters.get("mode_of_payment"):
conditions += """
AND EXISTS(
SELECT name FROM `tabSales Invoice Payment` sip
WHERE parent=p.name AND ifnull(sip.mode_of_payment, '') = %(mode_of_payment)s
)"""
-
+
return conditions
def get_group_by_field(group_by):
@@ -150,7 +152,7 @@
group_by_field = "customer"
elif group_by == "Payment Method":
group_by_field = "mode_of_payment"
-
+
return group_by_field
def get_columns(filters):
@@ -217,4 +219,4 @@
},
]
- return columns
\ No newline at end of file
+ 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 5d04824..ef799f6 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
@@ -2,11 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
- get_filtered_list_for_consolidated_report)
+
+from erpnext.accounts.report.financial_statements import (
+ get_columns,
+ get_data,
+ get_filtered_list_for_consolidated_report,
+ get_period_list,
+)
+
def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.html b/erpnext/accounts/report/profitability_analysis/profitability_analysis.html
index 40ba20c..d4ae54d 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.html
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.html
@@ -1 +1 @@
-{% include "accounts/report/financial_statements.html" %}
\ No newline at end of file
+{% include "accounts/report/financial_statements.html" %}
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index 48bd730..ad97808 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -2,10 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, getdate, formatdate, cstr
-from erpnext.accounts.report.financial_statements import filter_accounts, filter_out_zero_value_rows
+from frappe.utils import cstr, flt
+
+from erpnext.accounts.report.financial_statements import (
+ filter_accounts,
+ filter_out_zero_value_rows,
+)
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
value_fields = ("income", "expense", "gross_profit_loss")
diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js
index a95cfac..feab96f 100644
--- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js
+++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js
@@ -5,4 +5,4 @@
frappe.query_reports["Purchase Invoice Trends"] = {
filters: erpnext.get_purchase_trends_filters()
}
-});
\ No newline at end of file
+});
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 ad3783f..b26c733 100644
--- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
+++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.controllers.trends import get_columns,get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
@@ -11,4 +12,4 @@
conditions = get_columns(filters, "Purchase Invoice")
data = get_data(filters, conditions)
- return conditions["columns"], data
\ No newline at end of file
+ return conditions["columns"], data
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js
index f34ea57..aaf76c4 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.js
+++ b/erpnext/accounts/report/purchase_register/purchase_register.js
@@ -56,4 +56,4 @@
]
}
-erpnext.utils.add_dimensions('Purchase Register', 7);
\ No newline at end of file
+erpnext.utils.add_dimensions('Purchase Register', 7);
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 10edd41..5d4a03c 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -2,10 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _, msgprint
from frappe.utils import flt
-from frappe import msgprint, _
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
+
def execute(filters=None):
return _execute(filters)
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 e9e9c9c..adf6b29 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
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
+
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
+
def execute(filters=None):
columns = get_column()
args = get_args()
@@ -105,4 +107,4 @@
def get_args():
return {'doctype': 'Purchase Receipt', 'party': 'supplier',
- 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}
\ No newline at end of file
+ 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}
diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js
index 2d320f5..e3d43a7 100644
--- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js
+++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js
@@ -5,4 +5,4 @@
frappe.query_reports["Sales Invoice Trends"] = {
filters: erpnext.get_sales_trends_filters()
}
-});
\ No newline at end of file
+});
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 ee3992f..0ec54c9 100644
--- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
+++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.controllers.trends import get_columns,get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js
index 068926b..44e20e8 100644
--- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js
+++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js
@@ -42,4 +42,4 @@
"fieldtype": "Check"
},
]
-};
\ No newline at end of file
+};
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 ff77468..9ad7007 100644
--- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
+++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
@@ -1,6 +1,7 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import cstr
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 a51c427..9fd7bc3 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
@@ -2,11 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from erpnext.accounts.report.sales_payment_summary.sales_payment_summary import get_mode_of_payments, get_mode_of_payment_details
from frappe.utils import today
+
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.report.sales_payment_summary.sales_payment_summary import (
+ get_mode_of_payment_details,
+ get_mode_of_payments,
+)
test_dependencies = ["Sales Invoice"]
@@ -162,4 +168,4 @@
"price_list": "Standard Selling",
"item_code": item.item_code,
"price_list_rate": 10000
- }).insert()
\ No newline at end of file
+ }).insert()
diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js
index 85bbcea..2c9b01b 100644
--- a/erpnext/accounts/report/sales_register/sales_register.js
+++ b/erpnext/accounts/report/sales_register/sales_register.js
@@ -69,4 +69,3 @@
}
erpnext.utils.add_dimensions('Sales Register', 7);
-
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index 9099593..57b69aa 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -2,11 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt
-from frappe import msgprint, _
+from frappe import _, msgprint
from frappe.model.meta import get_field_precision
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
+from frappe.utils import flt
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
+
def execute(filters=None):
return _execute(filters)
@@ -84,7 +90,7 @@
# Add amount in unrealized account
for account in unrealized_profit_loss_accounts:
row.update({
- frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account)))
+ frappe.scrub(account+"_unrealized"): flt(internal_invoice_map.get((inv.name, account)))
})
# net total
@@ -258,6 +264,7 @@
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))
@@ -284,7 +291,7 @@
for account in unrealized_profit_loss_accounts:
unrealized_profit_loss_account_columns.append({
"label": account,
- "fieldname": frappe.scrub(account),
+ "fieldname": frappe.scrub(account+"_unrealized"),
"fieldtype": "Currency",
"options": "currency",
"width": 120
diff --git a/erpnext/accounts/report/share_balance/share_balance.py b/erpnext/accounts/report/share_balance/share_balance.py
index 9f22f81..03efc9e 100644
--- a/erpnext/accounts/report/share_balance/share_balance.py
+++ b/erpnext/accounts/report/share_balance/share_balance.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
+from frappe import _
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.py b/erpnext/accounts/report/share_ledger/share_ledger.py
index 3ed3c91..9be6622 100644
--- a/erpnext/accounts/report/share_ledger/share_ledger.py
+++ b/erpnext/accounts/report/share_ledger/share_ledger.py
@@ -3,9 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cstr, cint, getdate
-from frappe import msgprint, _
+from frappe import _
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py
index d2c23ee..00316ba 100644
--- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py
+++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.accounts.report.customer_ledger_summary.customer_ledger_summary import PartyLedgerSummaryReport
+
+from erpnext.accounts.report.customer_ledger_summary.customer_ledger_summary import (
+ PartyLedgerSummaryReport,
+)
+
def execute(filters=None):
args = {
"party_type": "Supplier",
"naming_by": ["Buying Settings", "supp_master_name"],
}
- return PartyLedgerSummaryReport(filters).run(args)
\ No newline at end of file
+ return PartyLedgerSummaryReport(filters).run(args)
diff --git a/erpnext/accounts/report/tax_detail/tax_detail.py b/erpnext/accounts/report/tax_detail/tax_detail.py
index 18436de..f03498d 100644
--- a/erpnext/accounts/report/tax_detail/tax_detail.py
+++ b/erpnext/accounts/report/tax_detail/tax_detail.py
@@ -3,8 +3,10 @@
# Contributed by Case Solved and sponsored by Nulight Studios
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
from frappe import _
# NOTE: Payroll is implemented using Journal Entries which are included as GL Entries
diff --git a/erpnext/accounts/report/tax_detail/test_tax_detail.py b/erpnext/accounts/report/tax_detail/test_tax_detail.py
index 743ddba..e74b905 100644
--- a/erpnext/accounts/report/tax_detail/test_tax_detail.py
+++ b/erpnext/accounts/report/tax_detail/test_tax_detail.py
@@ -1,16 +1,26 @@
from __future__ import unicode_literals
-import frappe
-import unittest
import datetime
import json
import os
-from frappe.utils import getdate, add_to_date, get_first_day, get_last_day, get_year_start, get_year_ending
+import unittest
+
+import frappe
+from frappe.utils import (
+ add_to_date,
+ get_first_day,
+ get_last_day,
+ get_year_ending,
+ get_year_start,
+ getdate,
+)
+
from .tax_detail import filter_match, save_custom_report
+
class TestTaxDetail(unittest.TestCase):
def load_testdocs(self):
- from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
+ from erpnext.accounts.utils import FiscalYearError, get_fiscal_year
datapath, _ = os.path.splitext(os.path.realpath(__file__))
with open(datapath + '.json', 'r') as fp:
docs = json.load(fp)
diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json
index dfc4b18..91f0798 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json
@@ -1,12 +1,15 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
+ "columns": [],
"creation": "2018-08-21 11:25:00.551823",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
+ "filters": [],
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-09-21 11:25:00.551823",
+ "modified": "2021-09-20 17:43:39.518851",
"modified_by": "Administrator",
"module": "Accounts",
"name": "TDS Computation Summary",
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 6b9df41..536df1f 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
@@ -1,10 +1,14 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
+
+from erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly import (
+ get_result,
+ get_tds_docs,
+)
from erpnext.accounts.utils import get_fiscal_year
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category \
- import get_advance_vouchers, get_debit_note_amount
+
def execute(filters=None):
validate_filters(filters)
@@ -12,9 +16,12 @@
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
columns = get_columns(filters)
- res = get_result(filters)
+ tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
- return columns, res
+ res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
+ final_result = group_by_supplier_and_category(res)
+
+ return columns, final_result
def validate_filters(filters):
''' Validate if dates are properly set and lie in the same fiscal year'''
@@ -28,81 +35,39 @@
filters["fiscal_year"] = from_year
-def get_result(filters):
- # if no supplier selected, fetch data for all tds applicable supplier
- # else fetch relevant data for selected supplier
- pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
- fields = ["name", pan+" as pan", "tax_withholding_category", "supplier_type", "supplier_name"]
+def group_by_supplier_and_category(data):
+ supplier_category_wise_map = {}
- if filters.supplier:
- filters.supplier = frappe.db.get_list('Supplier',
- {"name": filters.supplier}, fields)
- else:
- filters.supplier = frappe.db.get_list('Supplier',
- {"tax_withholding_category": ["!=", ""]}, fields)
+ 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.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)
+
+ final_result = get_final_result(supplier_category_wise_map)
+
+ return final_result
+
+
+def get_final_result(supplier_category_wise_map):
out = []
- for supplier in filters.supplier:
- tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
- rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
-
- if rate:
- rate = rate[0]
-
- try:
- account = [d.account for d in tds.accounts if d.company == filters.company][0]
-
- except IndexError:
- account = []
- total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
- filters.company, filters.from_date, filters.to_date, filters.fiscal_year)
-
- if total_invoiced_amount or tds_deducted:
- row = [supplier.pan, supplier.name]
-
- if filters.naming_series == 'Naming Series':
- row.append(supplier.supplier_name)
-
- row.extend([tds.name, supplier.supplier_type, rate, total_invoiced_amount, tds_deducted])
- out.append(row)
+ for key, value in supplier_category_wise_map.items():
+ out.append(value)
return out
-def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, fiscal_year):
- ''' calculate total invoice amount and total tds deducted for given supplier '''
-
- entries = frappe.db.sql("""
- select voucher_no, credit
- from `tabGL Entry`
- where party in (%s) and credit > 0
- and company=%s and is_cancelled = 0
- and posting_date between %s and %s
- """, (supplier, company, from_date, to_date), as_dict=1)
-
- supplier_credit_amount = flt(sum(d.credit for d in entries))
-
- vouchers = [d.voucher_no for d in entries]
- vouchers += get_advance_vouchers([supplier], company=company,
- from_date=from_date, to_date=to_date)
-
- tds_deducted = 0
- if vouchers:
- tds_deducted = flt(frappe.db.sql("""
- select sum(credit)
- from `tabGL Entry`
- where account=%s and posting_date between %s and %s
- and company=%s and credit > 0 and voucher_no in ({0})
- """.format(', '.join("'%s'" % d for d in vouchers)),
- (account, from_date, to_date, company))[0][0])
-
- date_range_filter = [fiscal_year, from_date, to_date]
-
- debit_note_amount = get_debit_note_amount([supplier], date_range_filter, company=company)
-
- total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
-
- return total_invoiced_amount, tds_deducted
-
def get_columns(filters):
columns = [
{
@@ -144,7 +109,7 @@
{
"label": _("TDS Rate %"),
"fieldname": "tds_rate",
- "fieldtype": "Float",
+ "fieldtype": "Percent",
"width": 90
},
{
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
index 72de318..ff2aa30 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
@@ -16,69 +16,6 @@
"label": __("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
- "get_query": function() {
- return {
- "filters": {
- "tax_withholding_category": ["!=", ""],
- }
- }
- },
- on_change: function() {
- frappe.query_report.set_filter_value("purchase_invoice", "");
- frappe.query_report.refresh();
- }
- },
- {
- "fieldname":"purchase_invoice",
- "label": __("Purchase Invoice"),
- "fieldtype": "Link",
- "options": "Purchase Invoice",
- "get_query": function() {
- return {
- "filters": {
- "name": ["in", frappe.query_report.invoices]
- }
- }
- },
- on_change: function() {
- let supplier = frappe.query_report.get_filter_value('supplier');
- if(!supplier) return; // return if no supplier selected
-
- // filter invoices based on selected supplier
- let invoices = [];
- frappe.query_report.invoice_data.map(d => {
- if(d.supplier==supplier)
- invoices.push(d.name)
- });
- frappe.query_report.invoices = invoices;
- frappe.query_report.refresh();
- }
- },
- {
- "fieldname":"purchase_order",
- "label": __("Purchase Order"),
- "fieldtype": "Link",
- "options": "Purchase Order",
- "get_query": function() {
- return {
- "filters": {
- "name": ["in", frappe.query_report.invoices]
- }
- }
- },
- on_change: function() {
- let supplier = frappe.query_report.get_filter_value('supplier');
- if(!supplier) return; // return if no supplier selected
-
- // filter invoices based on selected supplier
- let invoices = [];
- frappe.query_report.invoice_data.map(d => {
- if(d.supplier==supplier)
- invoices.push(d.name)
- });
- frappe.query_report.invoices = invoices;
- frappe.query_report.refresh();
- }
},
{
"fieldname":"from_date",
@@ -96,23 +33,5 @@
"reqd": 1,
"width": "60px"
}
- ],
-
- onload: function(report) {
- // fetch all tds applied invoices
- frappe.call({
- "method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders",
- callback: function(r) {
- let invoices = [];
-
- r.message.map(d => {
- invoices.push(d.name);
- });
-
- report["invoice_data"] = r.message.invoices;
- report["invoices"] = invoices;
-
- }
- });
- }
+ ]
}
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json
index 557a62d..4d555bd 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json
@@ -1,13 +1,15 @@
{
"add_total_row": 1,
+ "columns": [],
"creation": "2018-08-21 11:32:30.874923",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
+ "filters": [],
"idx": 0,
"is_standard": "Yes",
- "modified": "2019-09-24 13:46:16.473711",
+ "modified": "2021-09-20 12:05:50.387572",
"modified_by": "Administrator",
"module": "Accounts",
"name": "TDS Payable Monthly",
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 ceefa31..621b697 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -2,24 +2,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate
+
def execute(filters=None):
- filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user)
validate_filters(filters)
- set_filters(filters)
-
- # TDS payment entries
- payment_entries = get_payment_entires(filters)
+ tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
columns = get_columns(filters)
- if not filters.get("invoices"):
- return columns, []
- res = get_result(filters, payment_entries)
-
+ res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
return columns, res
def validate_filters(filters):
@@ -27,109 +21,59 @@
if filters.from_date > filters.to_date:
frappe.throw(_("From Date must be before To Date"))
-def set_filters(filters):
- invoices = []
-
- if not filters.get("invoices"):
- filters["invoices"] = get_tds_invoices_and_orders()
-
- if filters.supplier and filters.purchase_invoice:
- for d in filters["invoices"]:
- if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
- invoices.append(d)
- elif filters.supplier and not filters.purchase_invoice:
- for d in filters["invoices"]:
- if d.supplier == filters.supplier:
- invoices.append(d)
- elif filters.purchase_invoice and not filters.supplier:
- for d in filters["invoices"]:
- if d.name == filters.purchase_invoice:
- invoices.append(d)
- elif filters.supplier and filters.purchase_order:
- for d in filters.get("invoices"):
- if d.name == filters.purchase_order and d.supplier == filters.supplier:
- invoices.append(d)
- elif filters.supplier and not filters.purchase_order:
- for d in filters.get("invoices"):
- if d.supplier == filters.supplier:
- invoices.append(d)
- elif filters.purchase_order and not filters.supplier:
- for d in filters.get("invoices"):
- if d.name == filters.purchase_order:
- invoices.append(d)
-
- filters["invoices"] = invoices if invoices else filters["invoices"]
- filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
-
- #print(filters.get('invoices'))
-
-def get_result(filters, payment_entries):
- supplier_map, tds_docs = get_supplier_map(filters, payment_entries)
- documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries]
-
- gle_map = get_gle_map(filters, documents)
+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)
+ gle_map = get_gle_map(filters, tds_docs)
out = []
- for d in gle_map:
+ for name, details in gle_map.items():
tds_deducted, total_amount_credited = 0, 0
- supplier = supplier_map[d]
+ tax_withholding_category = tax_category_map.get(name)
+ rate = tax_rate_map.get(tax_withholding_category)
- tds_doc = tds_docs[supplier.tax_withholding_category]
- account_list = [i.account for i in tds_doc.accounts if i.company == filters.company]
+ for entry in details:
+ supplier = entry.party or entry.against
+ posting_date = entry.posting_date
+ voucher_type = entry.voucher_type
- if account_list:
- account = account_list[0]
+ if entry.account in tds_accounts:
+ tds_deducted += (entry.credit - entry.debit)
- for k in gle_map[d]:
- if k.party == supplier_map[d] and k.credit > 0:
- total_amount_credited += (k.credit - k.debit)
- elif account_list and k.account == account and (k.credit - k.debit) > 0:
- tds_deducted = (k.credit - k.debit)
- total_amount_credited += (k.credit - k.debit)
- voucher_type = k.voucher_type
+ total_amount_credited += (entry.credit - entry.debit)
- rate = [i.tax_withholding_rate for i in tds_doc.rates
- if i.fiscal_year == gle_map[d][0].fiscal_year]
-
- if rate and len(rate) > 0 and tds_deducted:
- rate = rate[0]
-
- row = [supplier.pan, supplier.name]
+ if rate and tds_deducted:
+ row = {
+ 'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier).pan,
+ 'supplier': supplier_map.get(supplier).name
+ }
if filters.naming_series == 'Naming Series':
- row.append(supplier.supplier_name)
+ row.update({'supplier_name': supplier_map.get(supplier).supplier_name})
- row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
- tds_deducted, gle_map[d][0].posting_date, voucher_type, d])
+ row.update({
+ 'section_code': tax_withholding_category,
+ 'entity_type': supplier_map.get(supplier).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_map(filters, payment_entries):
- # create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
- # pre-fetch all distinct applicable tds docs
- supplier_map, tds_docs = {}, {}
- pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
- supplier_list = [d.supplier for d in filters["invoices"]]
+def get_supplier_pan_map():
+ supplier_map = frappe._dict()
+ suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
- supplier_detail = frappe.db.get_all('Supplier',
- {"name": ["in", supplier_list]},
- ["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
+ for d in suppliers:
+ supplier_map[d.name] = d
- for d in filters["invoices"]:
- supplier_map[d.get("name")] = [k for k in supplier_detail
- if k.name == d.get("supplier")][0]
-
- for d in payment_entries:
- supplier_map[d.get("name")] = [k for k in supplier_detail
- if k.name == d.get("supplier")][0]
-
- for d in supplier_detail:
- if d.get("tax_withholding_category") not in tds_docs:
- tds_docs[d.get("tax_withholding_category")] = \
- frappe.get_doc("Tax Withholding Category", d.get("tax_withholding_category"))
-
- return supplier_map, tds_docs
+ return supplier_map
def get_gle_map(filters, documents):
# create gle_map of the form
@@ -139,10 +83,9 @@
gle = frappe.db.get_all('GL Entry',
{
"voucher_no": ["in", documents],
- 'is_cancelled': 0,
- 'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
+ "credit": (">", 0)
},
- ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"],
+ ["credit", "debit", "account", "voucher_no", "posting_date", "voucher_type", "against", "party"],
)
for d in gle:
@@ -232,39 +175,57 @@
return columns
-def get_payment_entires(filters):
- filter_dict = {
- 'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
- 'party_type': 'Supplier',
- 'apply_tax_withholding_amount': 1
+def get_tds_docs(filters):
+ tds_documents = []
+ purchase_invoices = []
+ payment_entries = []
+ journal_entries = []
+ tax_category_map = {}
+
+ tds_accounts = frappe.get_all("Tax Withholding Account", {'company': filters.get('company')},
+ pluck="account")
+
+ query_filters = {
+ "credit": ('>', 0),
+ "account": ("in", tds_accounts),
+ "posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
+ "is_cancelled": 0
}
- if filters.get('purchase_invoice') or filters.get('purchase_order'):
- parent = frappe.db.get_all('Payment Entry Reference',
- {'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent'])
+ if filters.get('supplier'):
+ query_filters.update({'against': filters.get('supplier')})
- filter_dict.update({'name': ('in', [d.get('parent') for d in parent])})
+ tds_docs = frappe.get_all("GL Entry", query_filters, ["voucher_no", "voucher_type", "against", "party"])
- payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'],
- filters=filter_dict)
+ for d in tds_docs:
+ if d.voucher_type == "Purchase Invoice":
+ purchase_invoices.append(d.voucher_no)
+ elif d.voucher_type == "Payment Entry":
+ payment_entries.append(d.voucher_no)
+ elif d.voucher_type == "Journal Entry":
+ journal_entries.append(d.voucher_no)
- return payment_entries
+ tds_documents.append(d.voucher_no)
-@frappe.whitelist()
-def get_tds_invoices_and_orders():
- # fetch tds applicable supplier and fetch invoices for these suppliers
- suppliers = [d.name for d in frappe.db.get_list("Supplier",
- {"tax_withholding_category": ["!=", ""]}, ["name"])]
+ if purchase_invoices:
+ get_tax_category_map(purchase_invoices, 'Purchase Invoice', tax_category_map)
- invoices = frappe.db.get_list("Purchase Invoice",
- {"supplier": ["in", suppliers]}, ["name", "supplier"])
+ if payment_entries:
+ get_tax_category_map(payment_entries, 'Payment Entry', tax_category_map)
- orders = frappe.db.get_list("Purchase Order",
- {"supplier": ["in", suppliers]}, ["name", "supplier"])
+ if journal_entries:
+ get_tax_category_map(journal_entries, 'Journal Entry', tax_category_map)
- invoices = invoices + orders
- invoices = [d for d in invoices if d.supplier]
+ return tds_documents, tds_accounts, tax_category_map
- frappe.cache().hset("invoices", frappe.session.user, invoices)
+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)))
- return invoices
+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)
+
+ return frappe._dict(rate_map)
\ No newline at end of file
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 8645d55..078b065 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -110,6 +110,3 @@
erpnext.utils.add_dimensions('Trial Balance', 6);
});
-
-
-
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 33360e2..d65bcc4 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -2,12 +2,21 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import flt, getdate, formatdate, cstr
-from erpnext.accounts.report.financial_statements \
- import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
+from frappe.utils import cstr, flt, formatdate, getdate
+
+import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
+from erpnext.accounts.report.financial_statements import (
+ filter_accounts,
+ filter_out_zero_value_rows,
+ set_gl_entries_by_account,
+)
value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit")
@@ -321,4 +330,4 @@
row[reverse_col] = abs(row[valid_col])
row[valid_col] = 0.0
else:
- row[reverse_col] = 0.0
\ No newline at end of file
+ row[reverse_col] = 0.0
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 78c7e43..8e24544 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
@@ -2,11 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, cint
+from frappe.utils import cint, flt
+
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
+
def execute(filters=None):
validate_filters(filters)
@@ -242,4 +245,4 @@
else:
show_party_name = True
- return show_party_name
\ No newline at end of file
+ return show_party_name
diff --git a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.js b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.js
index 811414a..f0ba78c 100644
--- a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.js
+++ b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.js
@@ -4,9 +4,10 @@
frappe.query_reports["Unpaid Expense Claim"] = {
"filters": [
{
- "fieldname":"employee",
+ "fieldname": "employee",
"label": __("Employee"),
- "fieldtype": "Link"
+ "fieldtype": "Link",
+ "options": "Employee"
}
]
}
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 eee620b..71fe4a2 100644
--- a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
+++ b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns, data = [], []
columns = get_columns()
@@ -20,11 +22,11 @@
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
- from
+ from
`tabExpense Claim` ec, `tabGL Entry` gle
where
gle.against_voucher_type = "Expense Claim" and gle.against_voucher = ec.name
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index ba461ed..2f9e957 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -1,9 +1,11 @@
from __future__ import unicode_literals
+
import frappe
+from frappe.utils import flt, formatdate, get_datetime_str
+
from erpnext import get_company_currency, get_default_company
-from erpnext.setup.utils import get_exchange_rate
from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_date
-from frappe.utils import cint, get_datetime_str, formatdate, flt
+from erpnext.setup.utils import get_exchange_rate
__exchange_rates = {}
@@ -98,15 +100,15 @@
if entry.get('credit'):
entry['credit'] = credit_in_account_currency
else:
- value = debit or credit
date = currency_info['report_date']
- converted_value = convert(value, presentation_currency, company_currency, 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_value
+ entry['debit'] = converted_debit_value
if entry.get('credit'):
- entry['credit'] = converted_value
+ entry['credit'] = converted_credit_value
converted_gl_list.append(entry)
diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py
index 628c8ce..c3f6d27 100644
--- a/erpnext/accounts/test/test_utils.py
+++ b/erpnext/accounts/test/test_utils.py
@@ -1,22 +1,52 @@
from __future__ import unicode_literals
+
import unittest
-from erpnext.accounts.party import get_party_shipping_address
+
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.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
class TestUtils(unittest.TestCase):
@classmethod
def setUpClass(cls):
super(TestUtils, cls).setUpClass()
- make_test_objects('Address', ADDRESS_RECORDS)
+ make_test_objects("Address", ADDRESS_RECORDS)
def test_get_party_shipping_address(self):
- address = get_party_shipping_address('Customer', '_Test Customer 1')
- self.assertEqual(address, '_Test Billing Address 2 Title-Billing')
+ address = get_party_shipping_address("Customer", "_Test Customer 1")
+ self.assertEqual(address, "_Test Billing Address 2 Title-Billing")
def test_get_party_shipping_address2(self):
- address = get_party_shipping_address('Customer', '_Test Customer 2')
- self.assertEqual(address, '_Test Shipping Address 2 Title-Shipping')
+ address = get_party_shipping_address("Customer", "_Test Customer 2")
+ self.assertEqual(address, "_Test Shipping Address 2 Title-Shipping")
+
+ def test_get_voucher_wise_gl_entry(self):
+
+ pr = make_purchase_receipt(
+ item_code="_Test Item",
+ posting_date="2021-02-01",
+ rate=100,
+ qty=1,
+ warehouse="Stores - TCP1",
+ company="_Test Company with perpetual inventory",
+ )
+
+ future_vouchers = get_future_stock_vouchers("2021-01-01", "00:00:00", for_items=["_Test Item"])
+
+ voucher_type_and_no = ("Purchase Receipt", pr.name)
+ self.assertTrue(
+ voucher_type_and_no in future_vouchers,
+ msg="get_future_stock_vouchers not returning correct value",
+ )
+
+ 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",
+ )
ADDRESS_RECORDS = [
@@ -28,12 +58,8 @@
"city": "Lagos",
"country": "Nigeria",
"links": [
- {
- "link_doctype": "Customer",
- "link_name": "_Test Customer 2",
- "doctype": "Dynamic Link"
- }
- ]
+ {"link_doctype": "Customer", "link_name": "_Test Customer 2", "doctype": "Dynamic Link"}
+ ],
},
{
"doctype": "Address",
@@ -43,12 +69,8 @@
"city": "Lagos",
"country": "Nigeria",
"links": [
- {
- "link_doctype": "Customer",
- "link_name": "_Test Customer 2",
- "doctype": "Dynamic Link"
- }
- ]
+ {"link_doctype": "Customer", "link_name": "_Test Customer 2", "doctype": "Dynamic Link"}
+ ],
},
{
"doctype": "Address",
@@ -59,12 +81,8 @@
"country": "Nigeria",
"is_shipping_address": "1",
"links": [
- {
- "link_doctype": "Customer",
- "link_name": "_Test Customer 2",
- "doctype": "Dynamic Link"
- }
- ]
+ {"link_doctype": "Customer", "link_name": "_Test Customer 2", "doctype": "Dynamic Link"}
+ ],
},
{
"doctype": "Address",
@@ -75,11 +93,7 @@
"country": "Nigeria",
"is_shipping_address": "1",
"links": [
- {
- "link_doctype": "Customer",
- "link_name": "_Test Customer 1",
- "doctype": "Dynamic Link"
- }
- ]
- }
+ {"link_doctype": "Customer", "link_name": "_Test Customer 1", "doctype": "Dynamic Link"}
+ ],
+ },
]
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 5b58e87..fdd8d09 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -4,21 +4,26 @@
from __future__ import unicode_literals
-import frappe, erpnext
-import frappe.defaults
-from frappe.utils import nowdate, cstr, flt, cint, now, getdate
-from frappe import throw, _
-from frappe.utils import formatdate, get_number_format_info
-from six import iteritems
-# imported to enable erpnext.accounts.utils.get_account_currency
-from erpnext.accounts.doctype.account.account import get_account_currency
-from frappe.model.meta import get_field_precision
+from json import loads
-from erpnext.stock.utils import get_stock_value_on
+import frappe
+import frappe.defaults
+from frappe import _, 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 six import string_types
+
+import erpnext
+
+# imported to enable erpnext.accounts.utils.get_account_currency
+from erpnext.accounts.doctype.account.account import get_account_currency # noqa
from erpnext.stock import get_warehouse_account_map
+from erpnext.stock.utils import get_stock_value_on
+
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
class FiscalYearError(frappe.ValidationError): pass
+class PaymentEntryUnlinkError(frappe.ValidationError): pass
@frappe.whitelist()
def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
@@ -340,29 +345,42 @@
def reconcile_against_document(args):
"""
- Cancel JV, Update aginst document, split if required and resubmit jv
+ Cancel PE or JV, Update against document, split if required and resubmit
"""
- for d in args:
+ # To optimize making GL Entry for PE or JV with multiple references
+ reconciled_entries = {}
+ for row in args:
+ if not reconciled_entries.get((row.voucher_type, row.voucher_no)):
+ reconciled_entries[(row.voucher_type, row.voucher_no)] = []
- check_if_advance_entry_modified(d)
- validate_allocated_amount(d)
+ reconciled_entries[(row.voucher_type, row.voucher_no)].append(row)
+
+ for key, entries in reconciled_entries.items():
+ voucher_type = key[0]
+ voucher_no = key[1]
# cancel advance entry
- doc = frappe.get_doc(d.voucher_type, d.voucher_no)
-
+ doc = frappe.get_doc(voucher_type, voucher_no)
+ frappe.flags.ignore_party_validation = True
doc.make_gl_entries(cancel=1, adv_adj=1)
- # update ref in advance entry
- if d.voucher_type == "Journal Entry":
- update_reference_in_journal_entry(d, doc)
- else:
- update_reference_in_payment_entry(d, doc)
+ for entry in entries:
+ check_if_advance_entry_modified(entry)
+ validate_allocated_amount(entry)
+ # update ref in advance entry
+ if voucher_type == "Journal Entry":
+ update_reference_in_journal_entry(entry, doc, do_not_save=True)
+ else:
+ update_reference_in_payment_entry(entry, doc, do_not_save=True)
+
+ doc.save(ignore_permissions=True)
# re-submit advance entry
- doc = frappe.get_doc(d.voucher_type, d.voucher_no)
+ doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
doc.make_gl_entries(cancel = 0, adv_adj =1)
+ frappe.flags.ignore_party_validation = False
- if d.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):
@@ -371,6 +389,9 @@
check if amount is same
check if jv is submitted
"""
+ 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("""
@@ -392,14 +413,14 @@
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.allocated_amount = %(unadjusted_amount)s
+ and t2.allocated_amount = %(unreconciled_amount)s
""".format(party_account_field), args)
else:
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 = %(unadjusted_amount)s
+ and unallocated_amount = %(unreconciled_amount)s
""".format(party_account_field), args)
if not ret:
@@ -412,58 +433,44 @@
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, jv_obj):
+def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
"""
Updates against document, if partial amount splits into rows
"""
- jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
- jv_detail.set(d["dr_or_cr"], d["allocated_amount"])
- jv_detail.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit',
- d["allocated_amount"]*flt(jv_detail.exchange_rate))
+ jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
- original_reference_type = jv_detail.reference_type
- original_reference_name = jv_detail.reference_name
-
- jv_detail.set("reference_type", d["against_voucher_type"])
- jv_detail.set("reference_name", d["against_voucher"])
-
- if d['allocated_amount'] < d['unadjusted_amount']:
- jvd = frappe.db.sql("""
- select cost_center, balance, against_account, is_advance,
- account_type, exchange_rate, account_currency
- from `tabJournal Entry Account` where name = %s
- """, d['voucher_detail_no'], as_dict=True)
-
+ 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_company_currency = amount_in_account_currency * flt(jvd[0]['exchange_rate'])
+ 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)
+ else:
+ journal_entry.remove(jv_detail)
- # new entry with balance amount
- ch = jv_obj.append("accounts")
- ch.account = d['account']
- ch.account_type = jvd[0]['account_type']
- ch.account_currency = jvd[0]['account_currency']
- ch.exchange_rate = jvd[0]['exchange_rate']
- ch.party_type = d["party_type"]
- ch.party = d["party"]
- ch.cost_center = cstr(jvd[0]["cost_center"])
- ch.balance = flt(jvd[0]["balance"])
+ # new row with references
+ new_row = journal_entry.append("accounts")
+ new_row.update(jv_detail.as_dict().copy())
- ch.set(d['dr_or_cr'], amount_in_account_currency)
- ch.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit', amount_in_company_currency)
+ 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))
- ch.set('credit_in_account_currency' if d['dr_or_cr']== 'debit_in_account_currency'
- else 'debit_in_account_currency', 0)
- ch.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)
- ch.against_account = cstr(jvd[0]["against_account"])
- ch.reference_type = original_reference_type
- ch.reference_name = original_reference_name
- ch.is_advance = cstr(jvd[0]["is_advance"])
- ch.docstatus = 1
+ new_row.set("reference_type", d["against_voucher_type"])
+ new_row.set("reference_name", d["against_voucher"])
+
+ new_row.against_account = cstr(jv_detail.against_account)
+ new_row.is_advance = cstr(jv_detail.is_advance)
+ new_row.docstatus = 1
# will work as update after submit
- jv_obj.flags.ignore_validate_update_after_submit = True
- jv_obj.save(ignore_permissions=True)
+ journal_entry.flags.ignore_validate_update_after_submit = True
+ 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 = {
@@ -553,10 +560,16 @@
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
for pe in linked_pe:
- pe_doc = frappe.get_doc("Payment Entry", pe)
- pe_doc.set_total_allocated_amount()
- pe_doc.set_unallocated_amount()
- pe_doc.clear_unallocated_reference_document_rows()
+ try:
+ pe_doc = frappe.get_doc("Payment Entry", pe)
+ pe_doc.set_amounts()
+ pe_doc.clear_unallocated_reference_document_rows()
+ 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 += _("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,
base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s
@@ -567,7 +580,7 @@
@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}")
@@ -777,16 +790,28 @@
if doctype == 'Account':
sort_accounts(acc, is_root, key="value")
- company_currency = frappe.get_cached_value('Company', company, "default_currency")
- for each in acc:
- each["company_currency"] = company_currency
- each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company))
-
- if each.account_currency != company_currency:
- each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company))
return acc
+@frappe.whitelist()
+def get_account_balances(accounts, company):
+
+ if isinstance(accounts, string_types):
+ accounts = loads(accounts)
+
+ if not accounts:
+ return []
+
+ 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))
+ 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
@@ -877,7 +902,9 @@
@frappe.whitelist()
def get_coa(doctype, parent, is_root, chart=None):
- from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import build_tree_from_json
+ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
+ build_tree_from_json,
+ )
# add chart to flags to retrieve when called from expand all function
chart = chart if chart else frappe.flags.chart
@@ -951,6 +978,9 @@
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]]
"""
gl_entries = {}
if not future_stock_vouchers:
@@ -959,7 +989,7 @@
voucher_nos = [d[1] for d in future_stock_vouchers]
gles = frappe.db.sql("""
- select name, account, credit, debit, cost_center, project
+ select name, account, credit, debit, cost_center, project, voucher_type, voucher_no
from `tabGL Entry`
where
posting_date >= %s and voucher_no in (%s)""" %
@@ -1077,3 +1107,14 @@
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
+ })
+ if icons:
+ for icon in icons:
+ frappe.delete_doc("Desktop Icon", icon)
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index b5bd14d..30ed58b 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -234,6 +234,15 @@
"type": "Link"
},
{
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Reconciliation",
+ "link_to": "Payment Reconciliation",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
"dependencies": "Sales Invoice",
"hidden": 0,
"is_query_report": 1,
@@ -341,6 +350,15 @@
"type": "Link"
},
{
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Reconciliation",
+ "link_to": "Payment Reconciliation",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
"dependencies": "Purchase Invoice",
"hidden": 0,
"is_query_report": 1,
@@ -516,6 +534,17 @@
"type": "Link"
},
{
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "KSA VAT Report",
+ "link_to": "KSA VAT",
+ "link_type": "Report",
+ "onboard": 0,
+ "only_for": "Saudi Arabia",
+ "type": "Link"
+ },
+ {
"hidden": 0,
"is_query_report": 0,
"label": "Financial Statements",
@@ -1136,6 +1165,16 @@
"type": "Link"
},
{
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "KSA VAT Setting",
+ "link_to": "KSA VAT Setting",
+ "link_type": "DocType",
+ "onboard": 0,
+ "only_for": "Saudi Arabia",
+ "type": "Link"
+ },
+ {
"hidden": 0,
"is_query_report": 0,
"label": "Profitability",
@@ -1188,7 +1227,7 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:52.872470",
+ "modified": "2021-08-26 13:15:52.872470",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
@@ -1249,4 +1288,4 @@
}
],
"title": "Accounting"
-}
\ No newline at end of file
+}
diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py b/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py
index 3bd3d7d..b0441c2 100644
--- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AgricultureAnalysisCriteria(Document):
pass
diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.js b/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.js
deleted file mode 100644
index f70dcd2..0000000
--- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Agriculture Analysis Criteria", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Agriculture Analysis Criteria
- () => frappe.tests.make('Agriculture Analysis Criteria', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py b/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py
index d79970b..4213e45 100644
--- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestAgricultureAnalysisCriteria(unittest.TestCase):
pass
diff --git a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py b/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py
index ce39368..642f491 100644
--- a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py
+++ b/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AgricultureTask(Document):
pass
diff --git a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.js b/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.js
deleted file mode 100644
index a012c4b..0000000
--- a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Agriculture Task", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Agriculture Task
- () => frappe.tests.make('Agriculture Task', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py b/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py
index e828151..2b3c338 100644
--- a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py
+++ b/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestAgricultureTask(unittest.TestCase):
pass
diff --git a/erpnext/agriculture/doctype/crop/crop.js b/erpnext/agriculture/doctype/crop/crop.js
index afd84fd..5508246 100644
--- a/erpnext/agriculture/doctype/crop/crop.js
+++ b/erpnext/agriculture/doctype/crop/crop.js
@@ -52,4 +52,4 @@
}
});
});
-};
\ No newline at end of file
+};
diff --git a/erpnext/agriculture/doctype/crop/crop_dashboard.py b/erpnext/agriculture/doctype/crop/crop_dashboard.py
index 9a8f26f..02b937a 100644
--- a/erpnext/agriculture/doctype/crop/crop_dashboard.py
+++ b/erpnext/agriculture/doctype/crop/crop_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'transactions': [
@@ -9,4 +11,4 @@
'items': ['Crop Cycle']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/agriculture/doctype/crop/test_crop.js b/erpnext/agriculture/doctype/crop/test_crop.js
index 138acbf..4055563 100644
--- a/erpnext/agriculture/doctype/crop/test_crop.js
+++ b/erpnext/agriculture/doctype/crop/test_crop.js
@@ -105,7 +105,7 @@
]
]}
]),
- // agriculture task list
+ // agriculture task list
() => {
assert.equal(cur_frm.doc.name, 'Basil from seed');
assert.equal(cur_frm.doc.period, 15);
diff --git a/erpnext/agriculture/doctype/crop/test_crop.py b/erpnext/agriculture/doctype/crop/test_crop.py
index c2e4917..1968a04 100644
--- a/erpnext/agriculture/doctype/crop/test_crop.py
+++ b/erpnext/agriculture/doctype/crop/test_crop.py
@@ -3,12 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
test_dependencies = ["Fertilizer"]
class TestCrop(unittest.TestCase):
def test_crop_period(self):
basil = frappe.get_doc('Crop', 'Basil from seed')
- self.assertEqual(basil.period, 15)
\ No newline at end of file
+ self.assertEqual(basil.period, 15)
diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js
index 464a368..87184da 100644
--- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js
+++ b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js
@@ -19,7 +19,7 @@
{disease: 'Aphids'}
]
]},
- {linked_land_unit: [
+ {linked_land_unit: [
[
{land_unit: 'Basil Farm'}
]
diff --git a/erpnext/agriculture/doctype/detected_disease/detected_disease.py b/erpnext/agriculture/doctype/detected_disease/detected_disease.py
index 8c90b83..b73fc32f 100644
--- a/erpnext/agriculture/doctype/detected_disease/detected_disease.py
+++ b/erpnext/agriculture/doctype/detected_disease/detected_disease.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class DetectedDisease(Document):
pass
diff --git a/erpnext/agriculture/doctype/disease/disease.py b/erpnext/agriculture/doctype/disease/disease.py
index c7707a5..e474efe 100644
--- a/erpnext/agriculture/doctype/disease/disease.py
+++ b/erpnext/agriculture/doctype/disease/disease.py
@@ -3,10 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe import _
+
class Disease(Document):
def validate(self):
@@ -17,4 +18,4 @@
frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name))
# to calculate the period of the Crop Cycle
if task.end_day > max_period: max_period = task.end_day
- self.treatment_period = max_period
\ No newline at end of file
+ self.treatment_period = max_period
diff --git a/erpnext/agriculture/doctype/disease/test_disease.js b/erpnext/agriculture/doctype/disease/test_disease.js
index 57d62c1..33f60c4 100644
--- a/erpnext/agriculture/doctype/disease/test_disease.js
+++ b/erpnext/agriculture/doctype/disease/test_disease.js
@@ -36,4 +36,3 @@
]);
});
-
diff --git a/erpnext/agriculture/doctype/disease/test_disease.py b/erpnext/agriculture/doctype/disease/test_disease.py
index 54788a2..1959d1f 100644
--- a/erpnext/agriculture/doctype/disease/test_disease.py
+++ b/erpnext/agriculture/doctype/disease/test_disease.py
@@ -3,10 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestDisease(unittest.TestCase):
def test_treatment_period(self):
disease = frappe.get_doc('Disease', 'Aphids')
- self.assertEqual(disease.treatment_period, 3)
\ No newline at end of file
+ self.assertEqual(disease.treatment_period, 3)
diff --git a/erpnext/agriculture/doctype/fertilizer/fertilizer.py b/erpnext/agriculture/doctype/fertilizer/fertilizer.py
index 9cb492a..75c2542 100644
--- a/erpnext/agriculture/doctype/fertilizer/fertilizer.py
+++ b/erpnext/agriculture/doctype/fertilizer/fertilizer.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class Fertilizer(Document):
@frappe.whitelist()
def load_contents(self):
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'})
for doc in docs:
- self.append('fertilizer_contents', {'title': str(doc.name)})
\ No newline at end of file
+ self.append('fertilizer_contents', {'title': str(doc.name)})
diff --git a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py
index 3a25b3f..c11c61a 100644
--- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py
+++ b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestFertilizer(unittest.TestCase):
def test_fertilizer_creation(self):
- self.assertEqual(frappe.db.exists('Fertilizer', 'Urea'), 'Urea')
\ No newline at end of file
+ self.assertEqual(frappe.db.exists('Fertilizer', 'Urea'), 'Urea')
diff --git a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py b/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py
index d385242..a050b71 100644
--- a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py
+++ b/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class FertilizerContent(Document):
pass
diff --git a/erpnext/agriculture/doctype/linked_location/linked_location.py b/erpnext/agriculture/doctype/linked_location/linked_location.py
index 3e49d3e..e622e84 100644
--- a/erpnext/agriculture/doctype/linked_location/linked_location.py
+++ b/erpnext/agriculture/doctype/linked_location/linked_location.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LinkedLocation(Document):
pass
diff --git a/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py b/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py
index daea54b..608bf8c 100644
--- a/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py
+++ b/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LinkedPlantAnalysis(Document):
pass
diff --git a/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py b/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py
index c4e9245..02eb133 100644
--- a/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py
+++ b/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LinkedSoilAnalysis(Document):
pass
diff --git a/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py b/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py
index 1b75892..f580e16 100644
--- a/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py
+++ b/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LinkedSoilTexture(Document):
pass
diff --git a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py
index 2806cc6..6238250 100644
--- a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py
+++ b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py
@@ -3,13 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.naming import make_autoname
from frappe.model.document import Document
+
class PlantAnalysis(Document):
@frappe.whitelist()
def load_contents(self):
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'})
for doc in docs:
- self.append('plant_analysis_criteria', {'title': str(doc.name)})
\ No newline at end of file
+ self.append('plant_analysis_criteria', {'title': str(doc.name)})
diff --git a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.js b/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.js
deleted file mode 100644
index 786c047..0000000
--- a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Plant Analysis", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Plant Analysis
- () => frappe.tests.make('Plant Analysis', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py b/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py
index cbd2fd7..6b6e843 100644
--- a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py
+++ b/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestPlantAnalysis(unittest.TestCase):
pass
diff --git a/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py b/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py
index c173184..9f71987 100644
--- a/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PlantAnalysisCriteria(Document):
pass
diff --git a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py
index 37835f8..e0c8177 100644
--- a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py
+++ b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class SoilAnalysis(Document):
@frappe.whitelist()
def load_contents(self):
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'})
for doc in docs:
- self.append('soil_analysis_criteria', {'title': str(doc.name)})
\ No newline at end of file
+ self.append('soil_analysis_criteria', {'title': str(doc.name)})
diff --git a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.js b/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.js
deleted file mode 100644
index 29128eb..0000000
--- a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Soil Analysis", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Soil Analysis
- () => frappe.tests.make('Soil Analysis', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py b/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py
index b89d756..24fe074 100644
--- a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py
+++ b/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestSoilAnalysis(unittest.TestCase):
pass
diff --git a/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py b/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py
index b073c20..09b917c 100644
--- a/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SoilAnalysisCriteria(Document):
pass
diff --git a/erpnext/agriculture/doctype/soil_texture/soil_texture.py b/erpnext/agriculture/doctype/soil_texture/soil_texture.py
index 209b2c8..636af77 100644
--- a/erpnext/agriculture/doctype/soil_texture/soil_texture.py
+++ b/erpnext/agriculture/doctype/soil_texture/soil_texture.py
@@ -3,11 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, cint
-from frappe import _
+from frappe.utils import cint, flt
+
class SoilTexture(Document):
soil_edit_order = [2, 1, 0]
diff --git a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py
index 937c06c..c701eb8 100644
--- a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py
+++ b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py
@@ -3,12 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestSoilTexture(unittest.TestCase):
def test_texture_selection(self):
soil_tex = frappe.get_all('Soil Texture', fields=['name'], filters={'collection_datetime': '2017-11-08'})
doc = frappe.get_doc('Soil Texture', soil_tex[0].name)
self.assertEqual(doc.silt_composition, 50)
- self.assertEqual(doc.soil_type, 'Silt Loam')
\ No newline at end of file
+ self.assertEqual(doc.soil_type, 'Silt Loam')
diff --git a/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py b/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py
index a7525ae..9980e8b 100644
--- a/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py
+++ b/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SoilTextureCriteria(Document):
pass
diff --git a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py b/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py
index b6467b7..5cddeeb 100644
--- a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py
+++ b/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestWaterAnalysis(unittest.TestCase):
pass
diff --git a/erpnext/agriculture/doctype/water_analysis/water_analysis.py b/erpnext/agriculture/doctype/water_analysis/water_analysis.py
index d9f007c..228ae34 100644
--- a/erpnext/agriculture/doctype/water_analysis/water_analysis.py
+++ b/erpnext/agriculture/doctype/water_analysis/water_analysis.py
@@ -3,10 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe import _
+
class WaterAnalysis(Document):
@frappe.whitelist()
@@ -24,4 +25,4 @@
if self.collection_datetime > self.laboratory_testing_datetime:
frappe.throw(_('Lab testing datetime cannot be before collection datetime'))
if self.laboratory_testing_datetime > self.result_datetime:
- frappe.throw(_('Lab result datetime cannot be before testing datetime'))
\ No newline at end of file
+ frappe.throw(_('Lab result datetime cannot be before testing datetime'))
diff --git a/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py b/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py
index 6833f90..8771733 100644
--- a/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py
+++ b/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class WaterAnalysisCriteria(Document):
pass
diff --git a/erpnext/agriculture/doctype/weather/test_weather.js b/erpnext/agriculture/doctype/weather/test_weather.js
deleted file mode 100644
index b5009a4..0000000
--- a/erpnext/agriculture/doctype/weather/test_weather.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Weather", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Weather
- () => frappe.tests.make('Weather', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/agriculture/doctype/weather/test_weather.py b/erpnext/agriculture/doctype/weather/test_weather.py
index b4ab3ae..1b4bab9 100644
--- a/erpnext/agriculture/doctype/weather/test_weather.py
+++ b/erpnext/agriculture/doctype/weather/test_weather.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestWeather(unittest.TestCase):
pass
diff --git a/erpnext/agriculture/doctype/weather/weather.py b/erpnext/agriculture/doctype/weather/weather.py
index 235e684..b41964d 100644
--- a/erpnext/agriculture/doctype/weather/weather.py
+++ b/erpnext/agriculture/doctype/weather/weather.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class Weather(Document):
@frappe.whitelist()
def load_contents(self):
diff --git a/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py b/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py
index 89db74c..42fcbcb 100644
--- a/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py
+++ b/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class WeatherParameter(Document):
pass
diff --git a/erpnext/agriculture/setup.py b/erpnext/agriculture/setup.py
index ab91343..75f07be 100644
--- a/erpnext/agriculture/setup.py
+++ b/erpnext/agriculture/setup.py
@@ -426,5 +426,5 @@
title='Degree Days',
standard=1,
linked_doctype='Weather')
- ]
+ ]
insert_record(records)
diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py
index 7f3c1de..39f0f1a 100644
--- a/erpnext/assets/dashboard_fixtures.py
+++ b/erpnext/assets/dashboard_fixtures.py
@@ -1,13 +1,16 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import frappe
import json
-from frappe.utils import nowdate, add_months, get_date_str
+
+import frappe
from frappe import _
+from frappe.utils import get_date_str, nowdate
+
from erpnext.accounts.dashboard_fixtures import _get_fiscal_year
from erpnext.buying.dashboard_fixtures import get_company_for_dashboards
+
def get_data():
fiscal_year = _get_fiscal_year(nowdate())
@@ -176,4 +179,4 @@
"filters_json": "[]",
"doctype": "Number Card"
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 922cc4a..da5778e 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -103,11 +103,11 @@
frm.trigger("create_asset_maintenance");
}, __("Manage"));
}
-
+
frm.add_custom_button(__("Repair Asset"), function() {
frm.trigger("create_asset_repair");
}, __("Manage"));
-
+
if (frm.doc.status != 'Fully Depreciated') {
frm.add_custom_button(__("Adjust Asset Value"), function() {
frm.trigger("create_asset_adjustment");
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index b7ca693..7e135be 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -3,18 +3,37 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext, math, json
+
+import json
+import math
+
+import frappe
from frappe import _
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ date_diff,
+ flt,
+ get_datetime,
+ get_last_day,
+ getdate,
+ month_diff,
+ nowdate,
+ today,
+)
from six import string_types
-from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days, get_last_day, get_datetime
-from frappe.model.document import Document
+
+import erpnext
+from erpnext.accounts.general_ledger import make_reverse_gl_entries
+from erpnext.assets.doctype.asset.depreciation import (
+ get_depreciation_accounts,
+ get_disposal_account_and_cost_center,
+)
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
-from erpnext.assets.doctype.asset.depreciation \
- import get_disposal_account_and_cost_center, get_depreciation_accounts
-from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
-from erpnext.accounts.utils import get_account_currency
from erpnext.controllers.accounts_controller import AccountsController
+
class Asset(AccountsController):
def validate(self):
self.validate_asset_values()
@@ -121,11 +140,6 @@
if self.is_existing_asset:
return
- docname = self.purchase_receipt or self.purchase_invoice
- if docname:
- doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
- date = frappe.db.get_value(doctype, docname, 'posting_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"))
@@ -176,16 +190,16 @@
for d in self.get('finance_books'):
self.validate_asset_finance_books(d)
-
+
start = self.clear_depreciation_schedule()
# value_after_depreciation - current Asset value
if d.value_after_depreciation:
value_after_depreciation = (flt(d.value_after_depreciation) -
- flt(self.opening_accumulated_depreciation))
+ flt(self.opening_accumulated_depreciation))
else:
value_after_depreciation = (flt(self.gross_purchase_amount) -
- flt(self.opening_accumulated_depreciation))
+ flt(self.opening_accumulated_depreciation))
d.value_after_depreciation = value_after_depreciation
@@ -321,10 +335,10 @@
def get_from_date(self, finance_book):
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'):
if schedule.finance_book == finance_book:
@@ -375,10 +389,6 @@
if cint(self.number_of_depreciations_booked) > cint(row.total_number_of_depreciations):
frappe.throw(_("Number of Depreciations Booked cannot be greater than Total Number of Depreciations"))
- if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(nowdate()):
- frappe.msgprint(_("Depreciation Row {0}: Depreciation Start Date is entered as past date")
- .format(row.idx), title=_('Warning'), indicator='red')
-
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))
@@ -425,9 +435,10 @@
if 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'))
+ asset_value_after_full_schedule = flt(
+ flt(self.gross_purchase_amount) -
+ flt(self.opening_accumulated_depreciation) -
+ 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):
@@ -546,7 +557,7 @@
cwip_account = None
try:
cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
- except:
+ except Exception:
# if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
if cwip_enabled:
raise
@@ -783,6 +794,7 @@
@frappe.whitelist()
def make_asset_movement(assets, purpose=None):
import json
+
from six import string_types
if isinstance(assets, string_types):
@@ -831,4 +843,4 @@
else:
depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
- return depreciation_amount
\ No newline at end of file
+ return depreciation_amount
diff --git a/erpnext/assets/doctype/asset/asset_dashboard.py b/erpnext/assets/doctype/asset/asset_dashboard.py
index a5cf238..cd04e1d 100644
--- a/erpnext/assets/doctype/asset/asset_dashboard.py
+++ b/erpnext/assets/doctype/asset/asset_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'non_standard_fieldnames': {
@@ -11,4 +12,4 @@
'items': ['Asset Movement']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js
index 02f39e0..4302cb2 100644
--- a/erpnext/assets/doctype/asset/asset_list.js
+++ b/erpnext/assets/doctype/asset/asset_list.js
@@ -50,4 +50,4 @@
});
});
},
-}
\ No newline at end of file
+}
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 8fdbbf9..6097910 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -3,10 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, today, getdate, cint
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts
+from frappe.utils import cint, flt, getdate, today
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_checks_for_pl_and_bs_accounts,
+)
+
def post_depreciation_entries(date=None):
# Return if automatic booking of asset depreciation is disabled
@@ -59,7 +64,7 @@
"credit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset",
"reference_name": asset.name,
- "cost_center": ""
+ "cost_center": depreciation_cost_center
}
debit_entry = {
diff --git a/erpnext/assets/doctype/asset/test_asset.js b/erpnext/assets/doctype/asset/test_asset.js
deleted file mode 100644
index 6119e38..0000000
--- a/erpnext/assets/doctype/asset/test_asset.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset
- () => frappe.tests.make('Asset', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index e23a715..7183ee7 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -3,14 +3,23 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, add_months
-from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
-from erpnext.assets.doctype.asset.asset import make_sales_invoice
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+import frappe
+from frappe.utils import add_days, add_months, cstr, flt, get_last_day, getdate, nowdate
+
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
-from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice
+from erpnext.assets.doctype.asset.asset import make_sales_invoice
+from erpnext.assets.doctype.asset.depreciation import (
+ post_depreciation_entries,
+ restore_asset,
+ scrap_asset,
+)
+from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+ make_purchase_invoice as make_invoice,
+)
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
class TestAsset(unittest.TestCase):
def setUp(self):
@@ -636,12 +645,18 @@
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=8000.0, location="Test Location")
+ finance_book = frappe.new_doc('Finance Book')
+ finance_book.finance_book_name = 'Income Tax'
+ finance_book.for_income_tax = 1
+ finance_book.insert(ignore_if_duplicate=1)
+
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 = '2030-07-12'
asset.purchase_date = '2030-01-01'
asset.append("finance_books", {
+ "finance_book": finance_book.name,
"expected_value_after_useful_life": 1000,
"depreciation_method": "Written Down Value",
"total_number_of_depreciations": 3,
@@ -763,4 +778,4 @@
company.save()
# Enable booking asset depreciation entry automatically
- frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
\ No newline at end of file
+ frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index 46620d5..1e56c01 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cint, get_link_to_form
from frappe.model.document import Document
+from frappe.utils import cint, get_link_to_form
+
class AssetCategory(Document):
def validate(self):
@@ -20,7 +22,7 @@
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)
-
+
def validate_account_currency(self):
account_types = [
'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account'
@@ -33,13 +35,13 @@
account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency")
if account_currency != company_currency:
invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) }))
-
+
for d in invalid_accounts:
frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.")
.format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)),
title=_("Invalid Account"))
-
+
def validate_account_types(self):
account_type_map = {
'fixed_asset_account': { 'account_type': 'Fixed Asset' },
@@ -59,12 +61,12 @@
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_type)),
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
+ 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))
@@ -93,4 +95,4 @@
account = frappe.db.get_value("Asset Category Account",
filters={"parent": asset_category, "company_name": company}, fieldname=fieldname)
- return account
\ No newline at end of file
+ return account
diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.js b/erpnext/assets/doctype/asset_category/test_asset_category.js
deleted file mode 100644
index 7e343b7..0000000
--- a/erpnext/assets/doctype/asset_category/test_asset_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Category
- () => frappe.tests.make('Asset Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.py b/erpnext/assets/doctype/asset_category/test_asset_category.py
index 39b79d6..53ec4ed 100644
--- a/erpnext/assets/doctype/asset_category/test_asset_category.py
+++ b/erpnext/assets/doctype/asset_category/test_asset_category.py
@@ -3,16 +3,18 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestAssetCategory(unittest.TestCase):
def test_mandatory_fields(self):
asset_category = frappe.new_doc("Asset Category")
asset_category.asset_category_name = "Computers"
-
+
self.assertRaises(frappe.MandatoryError, asset_category.insert)
-
+
asset_category.total_number_of_depreciations = 3
asset_category.frequency_of_depreciation = 3
asset_category.append("accounts", {
@@ -21,7 +23,7 @@
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
"depreciation_expense_account": "_Test Depreciations - _TC"
})
-
+
try:
asset_category.insert()
except frappe.DuplicateEntryError:
@@ -44,4 +46,4 @@
"depreciation_expense_account": "_Test Depreciations - _TC"
})
- self.assertRaises(frappe.ValidationError, asset_category.insert)
\ No newline at end of file
+ self.assertRaises(frappe.ValidationError, asset_category.insert)
diff --git a/erpnext/assets/doctype/asset_category_account/asset_category_account.py b/erpnext/assets/doctype/asset_category_account/asset_category_account.py
index 67925f4..66280ac 100644
--- a/erpnext/assets/doctype/asset_category_account/asset_category_account.py
+++ b/erpnext/assets/doctype/asset_category_account/asset_category_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssetCategoryAccount(Document):
pass
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
index bdc2acf..c4f0953 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssetFinanceBook(Document):
pass
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
index 70b8654..52996e9 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
@@ -97,4 +97,4 @@
}
});
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index a506dee..ec55fa4 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
+from frappe import _, throw
from frappe.desk.form import assign_to
-from frappe import throw, _
+from frappe.model.document import Document
from frappe.utils import add_days, add_months, add_years, getdate, nowdate
+
class AssetMaintenance(Document):
def validate(self):
for task in self.get('asset_maintenance_tasks'):
@@ -116,4 +118,4 @@
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)
\ No newline at end of file
+ (asset_name), as_dict=1)
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.js
deleted file mode 100644
index f9b38a1..0000000
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Maintenance", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Maintenance
- () => frappe.tests.make('Asset Maintenance', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
index 392fbdd..0f91508 100644
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
@@ -3,11 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, get_last_day, add_days
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+import frappe
+from frappe.utils import add_days, get_last_day, nowdate
+
from erpnext.assets.doctype.asset_maintenance.asset_maintenance import calculate_next_due_date
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
class TestAssetMaintenance(unittest.TestCase):
def setUp(self):
@@ -73,7 +76,7 @@
'doctype': 'Location',
'location_name': 'Test Location'
}).insert()
-
+
if not frappe.db.exists("Item", "Photocopier"):
meta = frappe.get_meta('Asset')
naming_series = meta.get_field("naming_series").options
@@ -157,6 +160,6 @@
company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC"
company.depreciation_cost_center = "_Test Cost Center - _TC"
company.save()
-
+
# Enable booking asset depreciation entry automatically
- frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
\ No newline at end of file
+ frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js
index c5db90a..bcdc3ac 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js
+++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js
@@ -12,4 +12,4 @@
};
});
}
-});
\ No newline at end of file
+});
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 34facd8..1f23936 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
+++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
-from frappe.utils import nowdate, getdate
+from frappe.model.document import Document
+from frappe.utils import getdate, nowdate
+
from erpnext.assets.doctype.asset_maintenance.asset_maintenance import calculate_next_due_date
+
class AssetMaintenanceLog(Document):
def validate(self):
if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in ["Completed", "Cancelled"]:
diff --git a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.js b/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.js
deleted file mode 100644
index 4e80184..0000000
--- a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Maintenance Log", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Maintenance Log
- () => frappe.tests.make('Asset Maintenance Log', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py
index a1ec879..7ad69e9 100644
--- a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py
+++ b/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py
@@ -5,5 +5,6 @@
import unittest
+
class TestAssetMaintenanceLog(unittest.TestCase):
pass
diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py
index 2a5666d..2280f55 100644
--- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py
+++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssetMaintenanceTask(Document):
pass
diff --git a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py
index f741a8f..46e9029 100644
--- a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py
+++ b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssetMaintenanceTeam(Document):
pass
diff --git a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.js b/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.js
deleted file mode 100644
index 41bf696..0000000
--- a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Maintenance Team", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Maintenance Team
- () => frappe.tests.make('Asset Maintenance Team', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py b/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py
index a0c0b14..8d757b7 100644
--- a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py
+++ b/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py
@@ -5,5 +5,6 @@
import unittest
+
class TestAssetMaintenanceTeam(unittest.TestCase):
pass
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js
index 06d8879..2df7db9 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.js
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.js
@@ -99,4 +99,4 @@
});
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index b2de250..901bdb5 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class AssetMovement(Document):
def validate(self):
self.validate_asset()
@@ -40,14 +42,14 @@
if current_location != 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 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")
if not d.to_employee:
frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset))
-
+
if self.purpose == 'Transfer':
if d.to_employee:
frappe.throw(_("Transferring cannot be done to an Employee. \
@@ -57,7 +59,7 @@
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':
# only when asset is bought and first entry is made
if not d.source_location and not (d.target_location or d.to_employee):
@@ -80,14 +82,14 @@
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))
def on_submit(self):
self.set_latest_location_in_asset()
-
+
def on_cancel(self):
self.set_latest_location_in_asset()
@@ -105,12 +107,12 @@
# In case of cancellation it corresponds to previous latest document's location, employee
latest_movement_entry = frappe.db.sql(
"""
- SELECT asm_item.target_location, asm_item.to_employee
+ SELECT asm_item.target_location, asm_item.to_employee
FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm
- WHERE
+ WHERE
asm_item.parent=asm.name and
asm_item.asset=%(asset)s and
- asm.company=%(company)s and
+ asm.company=%(company)s and
asm.docstatus=1 and {0}
ORDER BY
asm.transaction_date desc limit 1
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.js b/erpnext/assets/doctype/asset_movement/test_asset_movement.js
deleted file mode 100644
index b951576..0000000
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Movement", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Movement
- () => frappe.tests.make('Asset Movement', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
index 2b2d2b4..058bbd1 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -3,16 +3,16 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-import erpnext
-from erpnext.stock.doctype.item.test_item import make_item
-from frappe.utils import now, nowdate, get_last_day, add_days
+
+import frappe
+from frappe.utils import now
+
from erpnext.assets.doctype.asset.test_asset import create_asset_data
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
class TestAssetMovement(unittest.TestCase):
def setUp(self):
frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
diff --git a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
index 4c6aaab..24da371 100644
--- a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
+++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AssetMovementItem(Document):
pass
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index 1cebfff..18a56d3 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -59,7 +59,7 @@
if (frm.doc.repair_status == "Completed") {
frm.set_value('completion_date', frappe.datetime.now_datetime());
- }
+ }
}
});
@@ -68,4 +68,4 @@
var row = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate);
},
-});
\ No newline at end of file
+});
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index d32fdf7..99a7d9b 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -3,13 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import time_diff_in_hours, getdate, add_months, flt, cint
+from frappe.utils import add_months, cint, flt, getdate, time_diff_in_hours
+
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.assets.doctype.asset.asset import get_asset_account
from erpnext.controllers.accounts_controller import AccountsController
+
class AssetRepair(AccountsController):
def validate(self):
self.asset_doc = frappe.get_doc('Asset', self.asset)
@@ -18,7 +21,7 @@
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')
@@ -98,7 +101,7 @@
if self.capitalize_repair_cost:
row.value_after_depreciation -= self.repair_cost
-
+
def get_total_value_of_stock_consumed(self):
total_value_of_stock_consumed = 0
if self.get('stock_consumption'):
@@ -141,7 +144,7 @@
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
+ expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account
gl_entries.append(
self.get_gl_dict({
@@ -149,7 +152,7 @@
"credit": self.repair_cost,
"credit_in_account_currency": self.repair_cost,
"against": repair_and_maintenance_account,
- "voucher_type": self.doctype,
+ "voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
"posting_date": getdate(),
@@ -167,7 +170,7 @@
"credit": item.amount,
"credit_in_account_currency": item.amount,
"against": repair_and_maintenance_account,
- "voucher_type": self.doctype,
+ "voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
"posting_date": getdate(),
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair_list.js b/erpnext/assets/doctype/asset_repair/asset_repair_list.js
index f36fd2f..86376f4 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair_list.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair_list.js
@@ -10,4 +10,3 @@
}
}
};
-
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.js b/erpnext/assets/doctype/asset_repair/test_asset_repair.js
deleted file mode 100644
index 7424ffe..0000000
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Repair", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Repair
- () => frappe.tests.make('Asset Repair', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
index 30bbb37..9945a32 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -2,10 +2,18 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import nowdate, flt
+
import unittest
-from erpnext.assets.doctype.asset.test_asset import create_asset_data, create_asset, set_depreciation_settings_in_company
+
+import frappe
+from frappe.utils import flt, nowdate
+
+from erpnext.assets.doctype.asset.test_asset import (
+ create_asset,
+ create_asset_data,
+ set_depreciation_settings_in_company,
+)
+
class TestAssetRepair(unittest.TestCase):
def setUp(self):
@@ -41,7 +49,7 @@
self.assertEqual(total_repair_cost, asset_repair.repair_cost)
for item in asset_repair.stock_items:
total_repair_cost += item.total_value
-
+
self.assertEqual(total_repair_cost, asset_repair.total_repair_cost)
def test_repair_status_after_submit(self):
@@ -99,7 +107,7 @@
initial_num_of_depreciations = num_of_depreciations(asset)
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)
@@ -110,8 +118,8 @@
return asset.finance_books[0].total_number_of_depreciations
def create_asset_repair(**args):
- from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
args = frappe._dict(args)
@@ -139,7 +147,7 @@
})
asset_repair.insert(ignore_if_duplicate=True)
-
+
if args.submit:
asset_repair.repair_status = "Completed"
asset_repair.cost_center = "_Test Cost Center - _TC"
@@ -165,4 +173,4 @@
asset_repair.purchase_invoice = make_purchase_invoice().name
asset_repair.submit()
- return asset_repair
\ No newline at end of file
+ return asset_repair
diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py
index fa22a57..2a8d64e 100644
--- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py
+++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class AssetRepairConsumedItem(Document):
pass
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 2f6b5ee..2c11018 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -3,12 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, getdate, cint, date_diff, formatdate
-from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
from frappe.model.document import Document
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts
+from frappe.utils import cint, date_diff, flt, formatdate, getdate
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_checks_for_pl_and_bs_accounts,
+)
+from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
+
class AssetValueAdjustment(Document):
def validate(self):
diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.js
deleted file mode 100644
index 32831c6..0000000
--- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Asset Value Adjustment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Value Adjustment
- () => frappe.tests.make('Asset Value Adjustment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 03dc47b..52728d6 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
@@ -3,12 +3,17 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, get_last_day, add_days
+
+import frappe
+from frappe.utils import add_days, get_last_day, nowdate
+
from erpnext.assets.doctype.asset.test_asset import create_asset_data
+from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import (
+ get_current_asset_value,
+)
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
-from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value
+
class TestAssetValueAdjustment(unittest.TestCase):
def setUp(self):
@@ -91,4 +96,4 @@
"cost_center": args.cost_center or "Main - _TC"
}).insert()
- return doc
\ No newline at end of file
+ return doc
diff --git a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py
index 54fba3f..3199b7d 100644
--- a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py
+++ b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class DepreciationSchedule(Document):
pass
diff --git a/erpnext/assets/doctype/linked_location/linked_location.py b/erpnext/assets/doctype/linked_location/linked_location.py
index 3e49d3e..e622e84 100644
--- a/erpnext/assets/doctype/linked_location/linked_location.py
+++ b/erpnext/assets/doctype/linked_location/linked_location.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LinkedLocation(Document):
pass
diff --git a/erpnext/assets/doctype/location/location.py b/erpnext/assets/doctype/location/location.py
index 317894c..1430306 100644
--- a/erpnext/assets/doctype/location/location.py
+++ b/erpnext/assets/doctype/location/location.py
@@ -8,8 +8,6 @@
import math
import frappe
-from frappe import _
-from frappe.model.document import Document
from frappe.utils import flt
from frappe.utils.nestedset import NestedSet, update_nsm
diff --git a/erpnext/assets/doctype/location/location_tree.js b/erpnext/assets/doctype/location/location_tree.js
index b405afd..3e105f6 100644
--- a/erpnext/assets/doctype/location/location_tree.js
+++ b/erpnext/assets/doctype/location/location_tree.js
@@ -30,4 +30,4 @@
onload: function (treeview) {
treeview.make_tree();
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/assets/doctype/location/test_location.js b/erpnext/assets/doctype/location/test_location.js
deleted file mode 100644
index 3c06b63..0000000
--- a/erpnext/assets/doctype/location/test_location.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Location", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Location
- () => frappe.tests.make('Location', [
- // values to be set
- { location_name: 'Basil Farm' }
- ]),
- () => {
- assert.equal(cur_frm.doc.name, 'Basil Farm');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py
index 3d9e555..8fc5c9c 100644
--- a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py
+++ b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MaintenanceTeamMember(Document):
pass
diff --git a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.js b/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.js
deleted file mode 100644
index d942e2a..0000000
--- a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Maintenance Team Member", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Maintenance Team Member
- () => frappe.tests.make('Maintenance Team Member', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py b/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py
index c805e56..f8958c6 100644
--- a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py
+++ b/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py
@@ -5,5 +5,6 @@
import unittest
+
class TestMaintenanceTeamMember(unittest.TestCase):
pass
diff --git a/erpnext/assets/form_tour/asset/asset.json b/erpnext/assets/form_tour/asset/asset.json
new file mode 100644
index 0000000..7c47a38
--- /dev/null
+++ b/erpnext/assets/form_tour/asset/asset.json
@@ -0,0 +1,125 @@
+{
+ "creation": "2021-08-24 16:55:10.923434",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 16:55:10.923434",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset",
+ "owner": "Administrator",
+ "reference_doctype": "Asset",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "Select Naming Series based on which Asset ID will be generated",
+ "field": "",
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Naming Series",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Naming Series"
+ },
+ {
+ "description": "Select an Asset Item",
+ "field": "",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Item Code",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Item Code"
+ },
+ {
+ "description": "Select a Location",
+ "field": "",
+ "fieldname": "location",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Location",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Location"
+ },
+ {
+ "description": "Check Is Existing Asset",
+ "field": "",
+ "fieldname": "is_existing_asset",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Is Existing Asset",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Is Existing Asset?"
+ },
+ {
+ "description": "Set Available for use date",
+ "field": "",
+ "fieldname": "available_for_use_date",
+ "fieldtype": "Date",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Available-for-use Date",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Available For Use Date"
+ },
+ {
+ "description": "Set Gross purchase amount",
+ "field": "",
+ "fieldname": "gross_purchase_amount",
+ "fieldtype": "Currency",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Gross Purchase Amount",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Gross Purchase Amount"
+ },
+ {
+ "description": "Set Purchase Date",
+ "field": "",
+ "fieldname": "purchase_date",
+ "fieldtype": "Date",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Purchase Date",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Purchase Date"
+ },
+ {
+ "description": "Check Calculate Depreciation",
+ "field": "",
+ "fieldname": "calculate_depreciation",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Calculate Depreciation",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Calculate Depreciation"
+ },
+ {
+ "description": "Enter depreciation which has already been booked for this asset",
+ "field": "",
+ "fieldname": "opening_accumulated_depreciation",
+ "fieldtype": "Currency",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Opening Accumulated Depreciation",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Accumulated Depreciation"
+ }
+ ],
+ "title": "Asset"
+}
\ No newline at end of file
diff --git a/erpnext/assets/form_tour/asset_category/asset_category.json b/erpnext/assets/form_tour/asset_category/asset_category.json
new file mode 100644
index 0000000..0283444
--- /dev/null
+++ b/erpnext/assets/form_tour/asset_category/asset_category.json
@@ -0,0 +1,65 @@
+{
+ "creation": "2021-08-24 12:48:20.763173",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 12:48:20.763173",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Category",
+ "owner": "Administrator",
+ "reference_doctype": "Asset Category",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "Name Asset category. You can create categories based on Asset Types like Furniture, Property, Electronics etc.",
+ "field": "",
+ "fieldname": "asset_category_name",
+ "fieldtype": "Data",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Asset Category Name",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Asset Category Name"
+ },
+ {
+ "description": "Check to enable Capital Work in Progress accounting",
+ "field": "",
+ "fieldname": "enable_cwip_accounting",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Enable Capital Work in Progress Accounting",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Enable CWIP Accounting"
+ },
+ {
+ "description": "Add a row to define Depreciation Method and other details. Note that you can leave Finance Book blank to have it's accounting done in the primary books of accounts.",
+ "field": "",
+ "fieldname": "finance_books",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Finance Books",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Finance Book Detail"
+ },
+ {
+ "description": "Select the Fixed Asset and Depreciation accounts applicable for this Asset Category type",
+ "field": "",
+ "fieldname": "accounts",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Accounts",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Accounts"
+ }
+ ],
+ "title": "Asset Category"
+}
\ No newline at end of file
diff --git a/erpnext/assets/module_onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json
index 1086ab4..e6df88b 100644
--- a/erpnext/assets/module_onboarding/assets/assets.json
+++ b/erpnext/assets/module_onboarding/assets/assets.json
@@ -13,26 +13,26 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset",
"idx": 0,
"is_complete": 0,
- "modified": "2020-07-08 14:05:51.828497",
+ "modified": "2021-08-24 17:50:41.573281",
"modified_by": "Administrator",
"module": "Assets",
"name": "Assets",
"owner": "Administrator",
"steps": [
{
- "step": "Introduction to Assets"
+ "step": "Fixed Asset Accounts"
},
{
- "step": "Create a Fixed Asset Item"
+ "step": "Asset Category"
},
{
- "step": "Create an Asset Category"
+ "step": "Asset Item"
},
{
- "step": "Purchase an Asset Item"
+ "step": "Asset Purchase"
},
{
- "step": "Create an Asset"
+ "step": "Existing Asset"
}
],
"subtitle": "Assets, Depreciations, Repairs, and more.",
diff --git a/erpnext/assets/onboarding_step/asset_category/asset_category.json b/erpnext/assets/onboarding_step/asset_category/asset_category.json
new file mode 100644
index 0000000..033e866
--- /dev/null
+++ b/erpnext/assets/onboarding_step/asset_category/asset_category.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let's review existing Asset Category",
+ "creation": "2021-08-13 14:26:18.656303",
+ "description": "# Asset Category\n\nAn Asset Category classifies different assets of a Company.\n\nYou can create an Asset Category based on the type of assets. For example, all your desktops and laptops can be part of an Asset Category named \"Electronic Equipments\". Create a separate category for furniture. Also, you can update default properties for each category, like:\n - Depreciation type and duration\n - Fixed asset account\n - Depreciation account\n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 12:49:37.665239",
+ "modified_by": "Administrator",
+ "name": "Asset Category",
+ "owner": "Administrator",
+ "reference_document": "Asset Category",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Define Asset Category",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/asset_item/asset_item.json b/erpnext/assets/onboarding_step/asset_item/asset_item.json
new file mode 100644
index 0000000..8a174c5
--- /dev/null
+++ b/erpnext/assets/onboarding_step/asset_item/asset_item.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let's create a new Asset item",
+ "creation": "2021-08-13 14:27:07.277167",
+ "description": "# Asset Item\n\nAsset items are created based on Asset Category. You can create one or multiple items against once Asset Category. The sales and purchase transaction for Asset is done via Asset Item. ",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-16 13:59:18.362233",
+ "modified_by": "Administrator",
+ "name": "Asset Item",
+ "owner": "Administrator",
+ "reference_document": "Item",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Create an Asset Item",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/asset_purchase/asset_purchase.json b/erpnext/assets/onboarding_step/asset_purchase/asset_purchase.json
new file mode 100644
index 0000000..54611ed
--- /dev/null
+++ b/erpnext/assets/onboarding_step/asset_purchase/asset_purchase.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let's create a Purchase Receipt",
+ "creation": "2021-08-13 14:27:53.678621",
+ "description": "# Purchase an Asset\n\nAssets purchases process if done following the standard Purchase cycle. If capital work in progress is enabled in Asset Category, Asset will be created as soon as Purchase Receipt is created for it. You can quickly create a Purchase Receipt for Asset and see its impact on books of accounts.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 17:26:57.180637",
+ "modified_by": "Administrator",
+ "name": "Asset Purchase",
+ "owner": "Administrator",
+ "reference_document": "Purchase Receipt",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Purchase an Asset",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/existing_asset/existing_asset.json b/erpnext/assets/onboarding_step/existing_asset/existing_asset.json
new file mode 100644
index 0000000..052d5e8
--- /dev/null
+++ b/erpnext/assets/onboarding_step/existing_asset/existing_asset.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Add an existing Asset",
+ "creation": "2021-08-13 14:28:30.650459",
+ "description": "# Add an Existing Asset\n\nIf you are just starting with ERPNext, you will need to enter Assets you already possess. You can add them as existing fixed assets in ERPNext. Please note that you will have to make a Journal Entry separately updating the opening balance in the fixed asset account.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-16 14:03:48.850471",
+ "modified_by": "Administrator",
+ "name": "Existing Asset",
+ "owner": "Administrator",
+ "reference_document": "Asset",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Add an Existing Asset",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/onboarding_step/fixed_asset_accounts/fixed_asset_accounts.json b/erpnext/assets/onboarding_step/fixed_asset_accounts/fixed_asset_accounts.json
new file mode 100644
index 0000000..cebee7a
--- /dev/null
+++ b/erpnext/assets/onboarding_step/fixed_asset_accounts/fixed_asset_accounts.json
@@ -0,0 +1,21 @@
+{
+ "action": "Go to Page",
+ "action_label": "Let's walk-through Chart of Accounts to review setup",
+ "creation": "2021-08-13 14:23:09.297765",
+ "description": "# Fixed Asset Accounts\n\nWith the company, a host of fixed asset accounts are pre-configured. To ensure your asset transactions are leading to correct accounting entries, you can review and set up following asset accounts as per your business requirements.\n - Fixed asset accounts (Asset account)\n - Accumulated depreciation\n - Capital Work in progress (CWIP) account\n - Asset Depreciation account (Expense account)",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 17:46:37.646174",
+ "modified_by": "Administrator",
+ "name": "Fixed Asset Accounts",
+ "owner": "Administrator",
+ "path": "app/account/view/tree",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Review Fixed Asset Accounts",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
index 1a6ef54..75f42a9 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -76,7 +76,7 @@
fieldtype: "Link",
options: "Asset Category"
},
- {
+ {
fieldname:"finance_book",
label: __("Finance Book"),
fieldtype: "Link",
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 d1457b9..e370b9d 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -2,10 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cstr, today, flt, add_years, formatdate, getdate
-from erpnext.accounts.report.financial_statements import get_period_list, get_fiscal_year_data, validate_fiscal_year
+from frappe.utils import cstr, flt, formatdate, getdate
+
+from erpnext.accounts.report.financial_statements import (
+ get_fiscal_year_data,
+ get_period_list,
+ validate_fiscal_year,
+)
+
def execute(filters=None):
filters = frappe._dict(filters or {})
@@ -99,7 +106,7 @@
labels_values_map = {}
date_field = frappe.scrub(filters.date_based_on)
- period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
+ 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:
@@ -293,4 +300,4 @@
"options": "Location",
"width": 100
},
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index b9c77d5..b828a43 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -28,7 +28,7 @@
"fieldname": "supp_master_name",
"fieldtype": "Select",
"label": "Supplier Naming By",
- "options": "Supplier Name\nNaming Series"
+ "options": "Supplier Name\nNaming Series\nAuto Name"
},
{
"fieldname": "supplier_group",
@@ -123,7 +123,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-06-24 10:38:28.934525",
+ "modified": "2021-09-08 19:26:23.548837",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.py b/erpnext/buying/doctype/buying_settings/buying_settings.py
index a634a09..9e72c18 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.py
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.py
@@ -4,10 +4,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe.model.document import Document
+
class BuyingSettings(Document):
def validate(self):
for key in ["supplier_group", "supp_master_name", "maintain_same_rate", "buying_price_list"]:
diff --git a/erpnext/buying/doctype/buying_settings/test_buying_settings.py b/erpnext/buying/doctype/buying_settings/test_buying_settings.py
index bf6eec6..4998aeb 100644
--- a/erpnext/buying/doctype/buying_settings/test_buying_settings.py
+++ b/erpnext/buying/doctype/buying_settings/test_buying_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestBuyingSettings(unittest.TestCase):
pass
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 521432d..2005dac 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -425,7 +425,10 @@
status: ["!=", "Stopped"],
per_ordered: ["<", 100],
company: me.frm.doc.company
- }
+ },
+ allow_child_item_selection: true,
+ child_fielname: "items",
+ child_columns: ["item_code", "qty"]
})
}, __("Get Items From"));
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index bb0ad60..896208f 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -144,9 +144,7 @@
{
"fieldname": "supplier_section",
"fieldtype": "Section Break",
- "options": "fa fa-user",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-user"
},
{
"allow_on_submit": 1,
@@ -156,9 +154,7 @@
"hidden": 1,
"label": "Title",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "naming_series",
@@ -170,9 +166,7 @@
"options": "PUR-ORD-.YYYY.-",
"print_hide": 1,
"reqd": 1,
- "set_only_once": 1,
- "show_days": 1,
- "show_seconds": 1
+ "set_only_once": 1
},
{
"bold": 1,
@@ -186,18 +180,14 @@
"options": "Supplier",
"print_hide": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))",
"description": "Fetch items based on Default Supplier.",
"fieldname": "get_items_from_open_material_requests",
"fieldtype": "Button",
- "label": "Get Items from Open Material Requests",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Get Items from Open Material Requests"
},
{
"bold": 1,
@@ -206,9 +196,7 @@
"fieldtype": "Data",
"in_global_search": 1,
"label": "Supplier Name",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "company",
@@ -220,17 +208,13 @@
"options": "Company",
"print_hide": 1,
"remember_last_selected_value": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"print_width": "50%",
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -242,35 +226,27 @@
"oldfieldname": "transaction_date",
"oldfieldtype": "Date",
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"allow_on_submit": 1,
"fieldname": "schedule_date",
"fieldtype": "Date",
- "label": "Required By",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Required By"
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.docstatus===1",
"fieldname": "order_confirmation_no",
"fieldtype": "Data",
- "label": "Order Confirmation No",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Order Confirmation No"
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.order_confirmation_no",
"fieldname": "order_confirmation_date",
"fieldtype": "Date",
- "label": "Order Confirmation Date",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Order Confirmation Date"
},
{
"fieldname": "amended_from",
@@ -282,25 +258,19 @@
"oldfieldtype": "Data",
"options": "Purchase Order",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "drop_ship",
"fieldtype": "Section Break",
- "label": "Drop Ship",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Drop Ship"
},
{
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"bold": 1,
@@ -308,41 +278,31 @@
"fieldtype": "Data",
"label": "Customer Name",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_19",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "customer_contact_person",
"fieldtype": "Link",
"label": "Customer Contact",
- "options": "Contact",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Contact"
},
{
"fieldname": "customer_contact_display",
"fieldtype": "Small Text",
"hidden": 1,
"label": "Customer Contact",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "customer_contact_mobile",
"fieldtype": "Small Text",
"hidden": 1,
"label": "Customer Mobile No",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "customer_contact_email",
@@ -350,35 +310,27 @@
"hidden": 1,
"label": "Customer Contact Email",
"options": "Email",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
"fieldname": "section_addresses",
"fieldtype": "Section Break",
- "label": "Address and Contact",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Address and Contact"
},
{
"fieldname": "supplier_address",
"fieldtype": "Link",
"label": "Supplier Address",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "contact_person",
"fieldtype": "Link",
"label": "Supplier Contact",
"options": "Contact",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "address_display",
@@ -405,42 +357,32 @@
"label": "Contact Email",
"options": "Email",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "col_break_address",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "shipping_address",
"fieldtype": "Link",
"label": "Company Shipping Address",
"options": "Address",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "shipping_address_display",
"fieldtype": "Small Text",
"label": "Shipping Address Details",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"fieldname": "currency_and_price_list",
"fieldtype": "Section Break",
"label": "Currency and Price List",
- "options": "fa fa-tag",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-tag"
},
{
"fieldname": "currency",
@@ -450,9 +392,7 @@
"oldfieldtype": "Select",
"options": "Currency",
"print_hide": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "conversion_rate",
@@ -462,24 +402,18 @@
"oldfieldtype": "Currency",
"precision": "9",
"print_hide": 1,
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"fieldname": "cb_price_list",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "buying_price_list",
"fieldtype": "Link",
"label": "Price List",
"options": "Price List",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "price_list_currency",
@@ -487,18 +421,14 @@
"label": "Price List Currency",
"options": "Currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "plc_conversion_rate",
"fieldtype": "Float",
"label": "Price List Exchange Rate",
"precision": "9",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"default": "0",
@@ -507,9 +437,7 @@
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "sec_warehouse",
@@ -522,15 +450,11 @@
"fieldtype": "Link",
"label": "Set Target Warehouse",
"options": "Warehouse",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "col_break_warehouse",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"default": "No",
@@ -539,34 +463,27 @@
"in_standard_filter": 1,
"label": "Supply Raw Materials",
"options": "No\nYes",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"depends_on": "eval:doc.is_subcontracted==\"Yes\"",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
"label": "Supplier Warehouse",
- "options": "Warehouse",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Warehouse"
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"hide_border": 1,
"oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-shopping-cart"
},
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
"label": "Scan Barcode",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -576,34 +493,26 @@
"oldfieldname": "po_details",
"oldfieldtype": "Table",
"options": "Purchase Order Item",
- "reqd": 1,
- "show_days": 1,
- "show_seconds": 1
+ "reqd": 1
},
{
"collapsible": 1,
"fieldname": "section_break_48",
"fieldtype": "Section Break",
- "label": "Pricing Rules",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Pricing Rules"
},
{
"fieldname": "pricing_rules",
"fieldtype": "Table",
"label": "Purchase Order Pricing Rule",
"options": "Pricing Rule Detail",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible_depends_on": "supplied_items",
"fieldname": "raw_material_details",
"fieldtype": "Section Break",
- "label": "Raw Materials Supplied",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Raw Materials Supplied"
},
{
"fieldname": "supplied_items",
@@ -614,23 +523,17 @@
"oldfieldtype": "Table",
"options": "Purchase Order Item Supplied",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "sb_last_purchase",
- "fieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Section Break"
},
{
"fieldname": "total_qty",
"fieldtype": "Float",
"label": "Total Quantity",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_total",
@@ -638,9 +541,7 @@
"label": "Total (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_net_total",
@@ -651,24 +552,18 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_26",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "total",
"fieldtype": "Currency",
"label": "Total",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "net_total",
@@ -678,26 +573,20 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "total_net_weight",
"fieldtype": "Float",
"label": "Total Net Weight",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "taxes_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
- "options": "fa fa-money",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-money"
},
{
"fieldname": "taxes_and_charges",
@@ -706,24 +595,18 @@
"oldfieldname": "purchase_other_charges",
"oldfieldtype": "Link",
"options": "Purchase Taxes and Charges Template",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_50",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "shipping_rule",
"fieldtype": "Link",
"label": "Shipping Rule",
"options": "Shipping Rule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "section_break_52",
@@ -736,17 +619,13 @@
"label": "Purchase Taxes and Charges",
"oldfieldname": "purchase_tax_details",
"oldfieldtype": "Table",
- "options": "Purchase Taxes and Charges",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Purchase Taxes and Charges"
},
{
"collapsible": 1,
"fieldname": "sec_tax_breakup",
"fieldtype": "Section Break",
- "label": "Tax Breakup",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Tax Breakup"
},
{
"fieldname": "other_charges_calculation",
@@ -755,18 +634,14 @@
"no_copy": 1,
"oldfieldtype": "HTML",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "totals",
"fieldtype": "Section Break",
"label": "Taxes and Charges",
"oldfieldtype": "Section Break",
- "options": "fa fa-money",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-money"
},
{
"depends_on": "base_taxes_and_charges_added",
@@ -777,9 +652,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "base_taxes_and_charges_deducted",
@@ -790,9 +663,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "base_total_taxes_and_charges",
@@ -804,15 +675,11 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_39",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"depends_on": "taxes_and_charges_added",
@@ -823,9 +690,7 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "taxes_and_charges_deducted",
@@ -836,9 +701,7 @@
"oldfieldtype": "Currency",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "total_taxes_and_charges",
@@ -847,18 +710,14 @@
"label": "Total Taxes and Charges",
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "apply_discount_on",
"fieldname": "discount_section",
"fieldtype": "Section Break",
- "label": "Additional Discount",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Additional Discount"
},
{
"default": "Grand Total",
@@ -866,9 +725,7 @@
"fieldtype": "Select",
"label": "Apply Additional Discount On",
"options": "\nGrand Total\nNet Total",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "base_discount_amount",
@@ -876,32 +733,24 @@
"label": "Additional Discount Amount (Company Currency)",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_45",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "additional_discount_percentage",
"fieldtype": "Float",
"label": "Additional Discount Percentage",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "discount_amount",
"fieldtype": "Currency",
"label": "Additional Discount Amount",
"options": "currency",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "totals_section",
@@ -917,9 +766,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -929,9 +776,7 @@
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"description": "In Words will be visible once you save the Purchase Order.",
@@ -942,9 +787,7 @@
"oldfieldname": "in_words",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "base_rounded_total",
@@ -954,16 +797,12 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break4",
"fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Column Break"
},
{
"fieldname": "grand_total",
@@ -973,9 +812,7 @@
"oldfieldname": "grand_total_import",
"oldfieldtype": "Currency",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.disable_rounded_total",
@@ -985,26 +822,20 @@
"no_copy": 1,
"options": "currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "rounded_total",
"fieldtype": "Currency",
"label": "Rounded Total",
"options": "currency",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"default": "0",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
- "label": "Disable Rounded Total",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Disable Rounded Total"
},
{
"fieldname": "in_words",
@@ -1014,9 +845,7 @@
"oldfieldname": "in_words_import",
"oldfieldtype": "Data",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "advance_paid",
@@ -1025,25 +854,19 @@
"no_copy": 1,
"options": "party_account_currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
"fieldname": "payment_schedule_section",
"fieldtype": "Section Break",
- "label": "Payment Terms",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Payment Terms"
},
{
"fieldname": "payment_terms_template",
"fieldtype": "Link",
"label": "Payment Terms Template",
- "options": "Payment Terms Template",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Payment Terms Template"
},
{
"fieldname": "payment_schedule",
@@ -1051,9 +874,7 @@
"label": "Payment Schedule",
"no_copy": 1,
"options": "Payment Schedule",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -1062,9 +883,7 @@
"fieldtype": "Section Break",
"label": "Terms and Conditions",
"oldfieldtype": "Section Break",
- "options": "fa fa-legal",
- "show_days": 1,
- "show_seconds": 1
+ "options": "fa fa-legal"
},
{
"fieldname": "tc_name",
@@ -1073,27 +892,21 @@
"oldfieldname": "tc_name",
"oldfieldtype": "Link",
"options": "Terms and Conditions",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "terms",
"fieldtype": "Text Editor",
"label": "Terms and Conditions",
"oldfieldname": "terms",
- "oldfieldtype": "Text Editor",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Text Editor"
},
{
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Information",
- "oldfieldtype": "Section Break",
- "show_days": 1,
- "show_seconds": 1
+ "oldfieldtype": "Section Break"
},
{
"default": "Draft",
@@ -1108,9 +921,7 @@
"print_hide": 1,
"read_only": 1,
"reqd": 1,
- "search_index": 1,
- "show_days": 1,
- "show_seconds": 1
+ "search_index": 1
},
{
"fieldname": "ref_sq",
@@ -1121,9 +932,7 @@
"oldfieldtype": "Data",
"options": "Supplier Quotation",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "party_account_currency",
@@ -1133,24 +942,18 @@
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "inter_company_order_reference",
"fieldtype": "Link",
"label": "Inter Company Order Reference",
"options": "Sales Order",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"fieldname": "column_break_74",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"depends_on": "eval:!doc.__islocal",
@@ -1160,9 +963,7 @@
"label": "% Received",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:!doc.__islocal",
@@ -1172,9 +973,7 @@
"label": "% Billed",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"collapsible": 1,
@@ -1184,8 +983,6 @@
"oldfieldtype": "Column Break",
"print_hide": 1,
"print_width": "50%",
- "show_days": 1,
- "show_seconds": 1,
"width": "50%"
},
{
@@ -1196,9 +993,7 @@
"oldfieldname": "letter_head",
"oldfieldtype": "Select",
"options": "Letter Head",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1210,15 +1005,11 @@
"oldfieldtype": "Link",
"options": "Print Heading",
"print_hide": 1,
- "report_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "report_hide": 1
},
{
"fieldname": "column_break_86",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
@@ -1226,25 +1017,19 @@
"fieldname": "group_same_items",
"fieldtype": "Check",
"label": "Group same items",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "language",
"fieldtype": "Data",
"label": "Print Language",
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"collapsible": 1,
"fieldname": "subscription_section",
"fieldtype": "Section Break",
- "label": "Subscription Section",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Subscription Section"
},
{
"allow_on_submit": 1,
@@ -1252,9 +1037,7 @@
"fieldtype": "Date",
"label": "From Date",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1262,15 +1045,11 @@
"fieldtype": "Date",
"label": "To Date",
"no_copy": 1,
- "print_hide": 1,
- "show_days": 1,
- "show_seconds": 1
+ "print_hide": 1
},
{
"fieldname": "column_break_97",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "auto_repeat",
@@ -1279,35 +1058,27 @@
"no_copy": 1,
"options": "Auto Repeat",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval: doc.auto_repeat",
"fieldname": "update_auto_repeat_reference",
"fieldtype": "Button",
- "label": "Update Auto Repeat Reference",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Update Auto Repeat Reference"
},
{
"fieldname": "tax_category",
"fieldtype": "Link",
"label": "Tax Category",
- "options": "Tax Category",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Tax Category"
},
{
"depends_on": "supplied_items",
"fieldname": "set_reserve_warehouse",
"fieldtype": "Link",
"label": "Set Reserve Warehouse",
- "options": "Warehouse",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Warehouse"
},
{
"collapsible": 1,
@@ -1317,9 +1088,7 @@
},
{
"fieldname": "column_break_75",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "billing_address",
@@ -1352,6 +1121,7 @@
"fetch_from": "supplier.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -1360,25 +1130,21 @@
"default": "0",
"fieldname": "apply_tds",
"fieldtype": "Check",
- "label": "Apply Tax Withholding Amount",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Apply Tax Withholding Amount"
},
{
"depends_on": "eval: doc.apply_tds",
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"label": "Tax Withholding Category",
- "options": "Tax Withholding Category",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Tax Withholding Category"
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-30 15:17:53.663648",
+ "modified": "2021-09-28 13:10:47.955401",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index a0b1e07..ac86337 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -2,23 +2,30 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from frappe.utils import cstr, flt, cint
-from frappe import msgprint, _
-from frappe.model.mapper import get_mapped_doc
-from erpnext.controllers.buying_controller import BuyingController
-from erpnext.stock.doctype.item.item import get_last_purchase_details
-from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty
+
+import frappe
+from frappe import _, msgprint
from frappe.desk.notifications import clear_doctype_notifications
-from erpnext.buying.utils import validate_for_items, check_on_hold_or_closed_status
-from erpnext.stock.utils import get_bin
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, cstr, flt
+
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+ unlink_inter_company_doc,
+ update_linked_doc,
+ validate_inter_company_party,
+)
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
+ get_party_tax_withholding_details,
+)
from erpnext.accounts.party import get_party_account_currency
-from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
+from erpnext.controllers.buying_controller import BuyingController
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
-from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
-from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_party,
- update_linked_doc, unlink_inter_company_doc)
+from erpnext.stock.doctype.item.item import get_item_defaults, get_last_purchase_details
+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"
@@ -635,4 +642,4 @@
'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 ''
- })
\ No newline at end of file
+ })
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
index ab514da..af1dceb 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'purchase_order',
diff --git a/erpnext/buying/doctype/purchase_order/regional/india.js b/erpnext/buying/doctype/purchase_order/regional/india.js
index 42d3995..ef83f20 100644
--- a/erpnext/buying/doctype/purchase_order/regional/india.js
+++ b/erpnext/buying/doctype/purchase_order/regional/india.js
@@ -1,3 +1,3 @@
{% include "erpnext/regional/india/taxes.js" %}
-erpnext.setup_auto_gst_taxation('Purchase Order');
\ No newline at end of file
+erpnext.setup_auto_gst_taxation('Purchase Order');
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index d668c76..1453b8e 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -2,24 +2,31 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import unittest
-import frappe
-import json
-import frappe.defaults
-from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-from frappe.utils import flt, add_days, nowdate, getdate
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.buying.doctype.purchase_order.purchase_order \
- import (make_purchase_receipt, make_purchase_invoice as make_pi_from_po, make_rm_stock_entry as make_subcontract_transfer_entry)
-from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_pi_from_pr
-from erpnext.stock.doctype.material_request.test_material_request import make_material_request
-from erpnext.stock.doctype.material_request.material_request import make_purchase_order
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-from erpnext.controllers.accounts_controller import update_child_qty_rate
-from erpnext.controllers.status_updater import OverAllowanceError
-from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
-from erpnext.stock.doctype.batch.test_batch import make_new_batch
+import json
+import unittest
+
+import frappe
+from frappe.utils import add_days, flt, getdate, nowdate
+
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.buying.doctype.purchase_order.purchase_order import (
+ make_purchase_invoice as make_pi_from_po,
+)
+from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+from erpnext.buying.doctype.purchase_order.purchase_order import (
+ make_rm_stock_entry as make_subcontract_transfer_entry,
+)
+from erpnext.controllers.accounts_controller import update_child_qty_rate
+from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+from erpnext.stock.doctype.material_request.test_material_request import make_material_request
+from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+ make_purchase_invoice as make_pi_from_pr,
+)
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_receipt(self):
@@ -415,10 +422,12 @@
self.assertEqual(po.get("items")[0].received_qty, 9)
# Make return purchase receipt, purchase invoice and check quantity
- from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
- import make_purchase_receipt as make_purchase_receipt_return
- from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
- import make_purchase_invoice as make_purchase_invoice_return
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ make_purchase_invoice as make_purchase_invoice_return,
+ )
+ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import (
+ 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.items[0].purchase_order = po.name
@@ -484,8 +493,10 @@
def test_make_purchase_invoice_with_terms(self):
- from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
-
+ from erpnext.selling.doctype.sales_order.test_sales_order import (
+ automatically_fetch_payment_terms,
+ )
+
automatically_fetch_payment_terms()
po = create_purchase_order(do_not_save=True)
@@ -977,9 +988,14 @@
self.assertEqual(po_doc.items[0].blanket_order, None)
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
- from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
+ create_payment_terms_template,
+ )
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
- from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+ from erpnext.selling.doctype.sales_order.test_sales_order import (
+ automatically_fetch_payment_terms,
+ compare_payment_schedules,
+ )
automatically_fetch_payment_terms()
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js
index 5d19687..012b061 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js
@@ -77,4 +77,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js
index 8c0c144..bc3d767 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js
@@ -58,4 +58,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js
index 4e73ab8..83eb295 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js
@@ -44,4 +44,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js
index 1e54e50..a729dd9 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js
@@ -41,4 +41,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js
index bf2dfeb..b605e76 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js
@@ -36,4 +36,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js
index 96775eb..c258756 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js
@@ -40,4 +40,4 @@
() => frappe.timeout(0.3),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
index 39716ed..ccc383f 100644
--- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
+++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js
@@ -41,4 +41,4 @@
() => frappe.timeout(0.3),
() => done()
]);
-});
\ No newline at end of file
+});
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 132dd17..87cd575 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -10,6 +10,7 @@
"item_code",
"supplier_part_no",
"item_name",
+ "product_bundle",
"column_break_4",
"schedule_date",
"expected_delivery_date",
@@ -488,7 +489,6 @@
"no_copy": 1,
"options": "Sales Order",
"print_hide": 1,
- "read_only": 1,
"search_index": 1
},
{
@@ -830,13 +830,20 @@
"label": "Production Plan Sub Assembly Item",
"no_copy": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "product_bundle",
+ "fieldtype": "Link",
+ "label": "Product Bundle",
+ "options": "Product Bundle",
+ "read_only": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-28 19:22:22.715365",
+ "modified": "2021-08-30 20:06:26.712097",
"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 8bdcd47..a391a3d 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py
@@ -2,12 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe.model.document import Document
+
class PurchaseOrderItem(Document):
pass
def on_doctype_update():
- frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"])
\ No newline at end of file
+ frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"])
diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py
index 6caffbd..909faec 100644
--- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py
+++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PurchaseOrderItemSupplied(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py
index 1a76f0e..caec4e8 100644
--- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py
+++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PurchaseReceiptItemSupplied(Document):
- pass
\ No newline at end of file
+ pass
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 a4ce84e..5aa2d13 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -3,21 +3,24 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe import _
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils import get_url, cint
-from frappe.utils.user import get_user_fullname
-from frappe.utils.print_format import download_pdf
-from frappe.desk.form.load import get_attachments
-from frappe.core.doctype.communication.email import make
-from erpnext.accounts.party import get_party_account_currency, get_party_details
-from erpnext.stock.doctype.material_request.material_request import set_missing_values
-from erpnext.controllers.buying_controller import BuyingController
-from erpnext.buying.utils import validate_for_items
+import json
+
+import frappe
+from frappe import _
+from frappe.core.doctype.communication.email import make
+from frappe.desk.form.load import get_attachments
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import get_url
+from frappe.utils.print_format import download_pdf
+from frappe.utils.user import get_user_fullname
from six import string_types
+from erpnext.accounts.party import get_party_account_currency, get_party_details
+from erpnext.buying.utils import validate_for_items
+from erpnext.controllers.buying_controller import BuyingController
+from erpnext.stock.doctype.material_request.material_request import set_missing_values
+
STANDARD_USERS = ("Guest", "Administrator")
class RequestforQuotation(BuyingController):
@@ -391,12 +394,10 @@
@frappe.whitelist()
def get_supplier_tag():
- if not frappe.cache().hget("Supplier", "Tags"):
- filters = {"document_type": "Supplier"}
- tags = list(set(tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag))
- frappe.cache().hset("Supplier", "Tags", tags)
+ filters = {"document_type": "Supplier"}
+ tags = list(set(tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag))
- return frappe.cache().hget("Supplier", "Tags")
+ return tags
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
@@ -425,4 +426,4 @@
.format(filters.get("supplier"), filters.get("company"), conditions),
{"page_len": page_len, "start": start}, as_dict=1)
- return rfq_data
\ No newline at end of file
+ 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 6efbc78..0708cab 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,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -10,4 +10,4 @@
'items': ['Supplier Quotation']
},
]
- }
\ No newline at end of file
+ }
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 36f87b0..33fde8e 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
@@ -7,12 +7,16 @@
import frappe
from frappe.utils import nowdate
+
+from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
+ create_supplier_quotation,
+ make_supplier_quotation_from_rfq,
+)
+from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
+from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
-from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation_from_rfq
-from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
-from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
-from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
+
class TestRequestforQuotation(unittest.TestCase):
def test_quote_status(self):
diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
index 1fcfe75..75f85f8 100644
--- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js
@@ -73,4 +73,4 @@
() => frappe.click_button('Close'),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js
index 2e1652d..f06c3f3 100644
--- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js
+++ b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js
@@ -125,4 +125,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.py b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.py
index cc897af..35f3305 100644
--- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.py
+++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class RequestforQuotationItem(Document):
pass
diff --git a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.py b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.py
index 4b0bbbe..47c0deb 100644
--- a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.py
+++ b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class RequestforQuotationSupplier(Document):
pass
diff --git a/erpnext/buying/doctype/supplier/regional/india.js b/erpnext/buying/doctype/supplier/regional/india.js
index bd710e0..5f49a47 100644
--- a/erpnext/buying/doctype/supplier/regional/india.js
+++ b/erpnext/buying/doctype/supplier/regional/india.js
@@ -1,3 +1,3 @@
{% include "erpnext/regional/india/party.js" %}
-erpnext.setup_gst_reminder_button('Supplier');
\ No newline at end of file
+erpnext.setup_gst_reminder_button('Supplier');
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 1766c2c..7ee9196 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -24,7 +24,26 @@
}
}
});
+
+ frm.set_query("supplier_primary_contact", function(doc) {
+ return {
+ query: "erpnext.buying.doctype.supplier.supplier.get_supplier_primary_contact",
+ filters: {
+ "supplier": doc.name
+ }
+ };
+ });
+
+ frm.set_query("supplier_primary_address", function(doc) {
+ return {
+ filters: {
+ "link_doctype": "Supplier",
+ "link_name": doc.name
+ }
+ };
+ });
},
+
refresh: function (frm) {
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' }
@@ -78,6 +97,30 @@
});
},
+ supplier_primary_address: function(frm) {
+ if (frm.doc.supplier_primary_address) {
+ frappe.call({
+ method: 'frappe.contacts.doctype.address.address.get_address_display',
+ args: {
+ "address_dict": frm.doc.supplier_primary_address
+ },
+ callback: function(r) {
+ frm.set_value("primary_address", r.message);
+ }
+ });
+ }
+ if (!frm.doc.supplier_primary_address) {
+ frm.set_value("primary_address", "");
+ }
+ },
+
+ supplier_primary_contact: function(frm) {
+ if (!frm.doc.supplier_primary_contact) {
+ frm.set_value("mobile_no", "");
+ frm.set_value("email_id", "");
+ }
+ },
+
is_internal_supplier: function(frm) {
if (frm.doc.is_internal_supplier == 1) {
frm.toggle_reqd("represents_company", true);
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 38b8dfd..12a09cd 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -49,6 +49,13 @@
"address_html",
"column_break1",
"contact_html",
+ "primary_address_and_contact_detail_section",
+ "supplier_primary_contact",
+ "mobile_no",
+ "email_id",
+ "column_break_44",
+ "supplier_primary_address",
+ "primary_address",
"default_payable_accounts",
"accounts",
"default_tax_withholding_config",
@@ -378,6 +385,47 @@
"fieldname": "allow_purchase_invoice_creation_without_purchase_receipt",
"fieldtype": "Check",
"label": "Allow Purchase Invoice Creation Without Purchase Receipt"
+ },
+ {
+ "fieldname": "primary_address_and_contact_detail_section",
+ "fieldtype": "Section Break",
+ "label": "Primary Address and Contact Detail"
+ },
+ {
+ "description": "Reselect, if the chosen contact is edited after save",
+ "fieldname": "supplier_primary_contact",
+ "fieldtype": "Link",
+ "label": "Supplier Primary Contact",
+ "options": "Contact"
+ },
+ {
+ "fetch_from": "supplier_primary_contact.mobile_no",
+ "fieldname": "mobile_no",
+ "fieldtype": "Read Only",
+ "label": "Mobile No"
+ },
+ {
+ "fetch_from": "supplier_primary_contact.email_id",
+ "fieldname": "email_id",
+ "fieldtype": "Read Only",
+ "label": "Email Id"
+ },
+ {
+ "fieldname": "column_break_44",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "primary_address",
+ "fieldtype": "Text",
+ "label": "Primary Address",
+ "read_only": 1
+ },
+ {
+ "description": "Reselect, if the chosen address is edited after save",
+ "fieldname": "supplier_primary_address",
+ "fieldtype": "Link",
+ "label": "Supplier Primary Address",
+ "options": "Address"
}
],
"icon": "fa fa-user",
@@ -385,12 +433,12 @@
"image_field": "image",
"links": [
{
- "group": "Item Group",
- "link_doctype": "Supplier Item Group",
- "link_fieldname": "supplier"
+ "group": "Allowed Items",
+ "link_doctype": "Party Specific Item",
+ "link_fieldname": "party"
}
],
- "modified": "2021-05-18 15:10:11.087191",
+ "modified": "2021-09-06 17:37:56.522233",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index fd16b23..0ab0171 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -2,13 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
import frappe.defaults
-from frappe import msgprint, _
-from frappe.model.naming import set_name_by_naming_series
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+from frappe import _, msgprint
+from frappe.contacts.address_and_contact import (
+ delete_contact_and_address,
+ load_address_and_contact,
+)
+from frappe.model.naming import set_name_by_naming_series, set_name_from_naming_options
+
+from erpnext.accounts.party import get_dashboard_info, validate_party_accounts
from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
class Supplier(TransactionBase):
@@ -35,14 +40,21 @@
supp_master_name = frappe.defaults.get_global_default('supp_master_name')
if supp_master_name == 'Supplier Name':
self.name = self.supplier_name
- else:
+ 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.create_primary_contact()
+ self.create_primary_address()
+
def validate(self):
+ 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 not self.naming_series:
@@ -76,7 +88,40 @@
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
+
+ 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)
+
+ 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'):
+ address = make_address(self)
+ address_display = get_address_display(address.name)
+
+ self.db_set("supplier_primary_address", address.name)
+ self.db_set("primary_address", address_display)
+
def on_trash(self):
+ if self.supplier_primary_contact:
+ frappe.db.sql("""
+ UPDATE `tabSupplier`
+ SET
+ supplier_primary_contact=null,
+ supplier_primary_address=null,
+ mobile_no=null,
+ email_id=null,
+ primary_address=null
+ WHERE name=%(name)s""", {"name": self.name})
+
delete_contact_and_address('Supplier', self.name)
def after_rename(self, olddn, newdn, merge=False):
@@ -104,3 +149,21 @@
doc.name, args.get('supplier_email_' + str(i)))
except frappe.NameError:
pass
+
+@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("""
+ SELECT
+ `tabContact`.name from `tabContact`,
+ `tabDynamic Link`
+ WHERE
+ `tabContact`.name = `tabDynamic Link`.parent
+ and `tabDynamic Link`.link_name = %(supplier)s
+ and `tabDynamic Link`.link_doctype = 'Supplier'
+ and `tabContact`.name like %(txt)s
+ """, {
+ 'supplier': supplier,
+ 'txt': '%%%s%%' % txt
+ })
diff --git a/erpnext/buying/doctype/supplier/test_supplier.js b/erpnext/buying/doctype/supplier/test_supplier.js
index bf7c192..eaa4d09 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.js
+++ b/erpnext/buying/doctype/supplier/test_supplier.js
@@ -74,4 +74,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 8980466..8a4eefa 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+import unittest
-import frappe, unittest
+import frappe
+from frappe.test_runner import make_test_records
+
from erpnext.accounts.party import get_due_date
from erpnext.exceptions import PartyDisabled
-from frappe.test_runner import make_test_records
test_dependencies = ['Payment Term', 'Payment Terms Template']
test_records = frappe.get_test_records('Supplier')
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/buying/doctype/supplier_item_group/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/buying/doctype/supplier_item_group/__init__.py
+++ /dev/null
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js
deleted file mode 100644
index f7da90d..0000000
--- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Supplier Item Group', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json
deleted file mode 100644
index 1971458..0000000
--- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json
+++ /dev/null
@@ -1,77 +0,0 @@
-{
- "actions": [],
- "creation": "2021-05-07 18:16:40.621421",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "supplier",
- "item_group"
- ],
- "fields": [
- {
- "fieldname": "supplier",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Supplier",
- "options": "Supplier",
- "reqd": 1
- },
- {
- "fieldname": "item_group",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Item Group",
- "options": "Item Group",
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-05-19 13:48:16.742303",
- "modified_by": "Administrator",
- "module": "Buying",
- "name": "Supplier Item Group",
- "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": "Purchase User",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Purchase Manager",
- "share": 1,
- "write": 1
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py
deleted file mode 100644
index 3a2e5d6..0000000
--- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-class SupplierItemGroup(Document):
- def validate(self):
- exists = frappe.db.exists({
- 'doctype': 'Supplier Item Group',
- 'supplier': self.supplier,
- 'item_group': self.item_group
- })
- if exists:
- frappe.throw(_("Item Group has already been linked to this supplier."))
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py
deleted file mode 100644
index c75044d..0000000
--- a/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestSupplierItemGroup(unittest.TestCase):
- pass
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 6a4c02c..af462fc 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -2,13 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, nowdate, add_days, getdate
from frappe.model.mapper import get_mapped_doc
+from frappe.utils import flt, getdate, nowdate
-from erpnext.controllers.buying_controller import BuyingController
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"
@@ -166,4 +167,4 @@
`tabSupplier Quotation` SET `status` = 'Expired'
WHERE
`status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s
- """, (nowdate()))
\ No newline at end of file
+ """, (nowdate()))
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
index 6b40305..014b102 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'supplier_quotation',
diff --git a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
index 6f34ca6..2db8e22 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
@@ -3,9 +3,11 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-import frappe.defaults
+
class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_order(self):
diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
index 2d2b29c..20fb430 100644
--- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js
@@ -71,4 +71,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js
index b151824..0a51565 100644
--- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js
+++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js
@@ -31,4 +31,4 @@
() => frappe.timeout(0.3),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js
index e37731e..7ea3e60 100644
--- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js
+++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js
@@ -34,4 +34,4 @@
() => frappe.timeout(0.3),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py
index 64dda87..03adab5 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class SupplierQuotationItem(Document):
pass
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
index 5f5f54b..b4cd852 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
@@ -93,5 +93,3 @@
}
});
};
-
-
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
index e956afd..f944fe4 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -3,13 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import throw, _
-from frappe.model.document import Document
+
import time
from datetime import timedelta
-from frappe.utils import nowdate, get_last_day, getdate, add_days, add_years
-from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import make_supplier_scorecard
+
+import frappe
+from frappe import _, throw
+from frappe.model.document import Document
+from frappe.utils import add_days, add_years, get_last_day, getdate, nowdate
+
+from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import (
+ make_supplier_scorecard,
+)
+
class SupplierScorecard(Document):
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
index 3d2305e..7186e01 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
@@ -13,4 +15,4 @@
'items': ['Supplier Scorecard Period']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
index 2528240..ef7fae3 100644
--- a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestSupplierScorecard(unittest.TestCase):
def test_create_scorecard(self):
@@ -128,4 +130,3 @@
"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 33a0dc7..c456377 100644
--- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import re
+
import frappe
from frappe import _
-import re
from frappe.model.document import Document
+
class InvalidFormulaVariable(frappe.ValidationError): pass
class SupplierScorecardCriteria(Document):
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 4eef4b4..9fca9a9 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
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestSupplierScorecardCriteria(unittest.TestCase):
def test_variables_exist(self):
delete_test_scorecards()
@@ -72,4 +74,4 @@
"criteria_name":"Fake Criteria 3",
"max_score":100.0
},
-]
\ No newline at end of file
+]
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 9938710..b03d216 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
@@ -3,12 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import throw, _
+from frappe import _, throw
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
+
import erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable as variable_functions
-from erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria import get_variables
+from erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria import (
+ get_variables,
+)
+
class SupplierScorecardPeriod(Document):
@@ -109,4 +114,3 @@
}, target_doc, post_process, ignore_permissions=True)
return doc
-
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py
index 8baa318..de8bc0a 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py
+++ b/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSupplierScorecardPeriod(unittest.TestCase):
pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py
index b64abed..79d5082 100644
--- a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SupplierScorecardScoringCriteria(Document):
pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py
index e8ad79f..5063b20 100644
--- a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SupplierScorecardScoringStanding(Document):
pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py
index 58a8a99..476cb35 100644
--- a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SupplierScorecardScoringVariable(Document):
pass
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 1ba5d06..4fc45e8 100644
--- a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class SupplierScorecardStanding(Document):
pass
@@ -26,4 +28,4 @@
`tabSupplier Scorecard Standing` scs""",
{}, as_dict=1)
- return standings
\ No newline at end of file
+ return standings
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py
index 4d96651..5ac5927 100644
--- a/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSupplierScorecardStanding(unittest.TestCase):
pass
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 37fdc57..30b2a18 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import sys
+
+import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
+
class VariablePathNotFound(frappe.ValidationError): pass
class SupplierScorecardVariable(Document):
@@ -18,7 +21,9 @@
def validate_path_exists(self):
if '.' in self.path:
try:
- from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import import_string_path
+ 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)
@@ -493,4 +498,4 @@
total_sq_days = 0
- return total_sq_days
\ No newline at end of file
+ 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 fe6dde5..990413c 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
@@ -3,10 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import VariablePathNotFound
+import frappe
+
+from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import (
+ VariablePathNotFound,
+)
class TestSupplierScorecardVariable(unittest.TestCase):
@@ -54,4 +57,4 @@
"variable_label":"Fake Variable 1",
"path":"get_fake_variable1"
},
-]
\ No newline at end of file
+]
diff --git a/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json b/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json
index 0c7cad6..2e2a048 100644
--- a/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json
+++ b/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json
@@ -13,6 +13,6 @@
"name": "Drop Shipping Format",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
index beeca09..cb99234 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
@@ -296,4 +298,4 @@
{conditions}
GROUP BY
parent.name, child.item_code
- """.format(conditions=conditions), as_dict=1) #nosec
\ No newline at end of file
+ """.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 44ab767..a5b0947 100644
--- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -2,15 +2,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import unittest
from datetime import datetime
+
import frappe
-from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
-from erpnext.stock.doctype.material_request.test_material_request import make_material_request
-from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
+from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+from erpnext.stock.doctype.material_request.test_material_request import make_material_request
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
class TestProcurementTracker(unittest.TestCase):
def test_result_for_procurement_tracker(self):
filters = {
@@ -68,4 +72,4 @@
"actual_delivery_date": date_obj
}
- return expected_data
\ No newline at end of file
+ return expected_data
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.py b/erpnext/buying/report/purchase_analytics/purchase_analytics.py
index 0f94947..bef66da 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.py
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.py
@@ -2,7 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from erpnext.selling.report.sales_analytics.sales_analytics import Analytics
+
def execute(filters=None):
return Analytics(filters).run()
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
index 701da43..ca3be03 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
@@ -30,7 +30,14 @@
"default": frappe.datetime.get_today()
},
{
- "fieldname": "purchase_order",
+ "fieldname":"project",
+ "label": __("Project"),
+ "fieldtype": "Link",
+ "width": "80",
+ "options": "Project"
+ },
+ {
+ "fieldname": "name",
"label": __("Purchase Order"),
"fieldtype": "Link",
"width": "80",
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 89be622..1b25dd4 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import copy
+
+import frappe
from frappe import _
-from frappe.utils import flt, date_diff, getdate
+from frappe.utils import date_diff, flt, getdate
+
def execute(filters=None):
if not filters:
@@ -38,14 +41,12 @@
if filters.get("from_date") and filters.get("to_date"):
conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
- if filters.get("company"):
- conditions += " and po.company = %(company)s"
+ for field in ['company', 'name', 'status']:
+ if filters.get(field):
+ conditions += f" and po.{field} = %({field})s"
- if filters.get("purchase_order"):
- conditions += " and po.name = %(purchase_order)s"
-
- if filters.get("status"):
- conditions += " and po.status in %(status)s"
+ if filters.get('project'):
+ conditions += " and poi.project = %(project)s"
return conditions
@@ -54,6 +55,7 @@
SELECT
po.transaction_date as date,
poi.schedule_date as required_date,
+ poi.project,
po.name as purchase_order,
po.status, po.supplier, poi.item_code,
poi.qty, poi.received_qty,
@@ -172,6 +174,12 @@
"fieldtype": "Link",
"options": "Supplier",
"width": 130
+ },{
+ "label": _("Project"),
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "options": "Project",
+ "width": 130
}]
if not filters.get("group_by_po"):
@@ -268,4 +276,3 @@
])
return columns
-
diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js
index 83d25d8..90919dc 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js
@@ -5,4 +5,4 @@
frappe.query_reports["Purchase Order Trends"] = {
filters: erpnext.get_purchase_trends_filters()
}
-});
\ No newline at end of file
+});
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 1ed6cad..9781480 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
-from erpnext.controllers.trends import get_columns,get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
@@ -55,4 +57,4 @@
"lineOptions": {
"regionFill": 1
}
- }
\ No newline at end of file
+ }
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 faf67c9..42cc6eb 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
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import copy
+
+import frappe
from frappe import _
-from frappe.utils import flt, date_diff, getdate
+from frappe.utils import date_diff, flt, getdate
+
def execute(filters=None):
if not filters:
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 0c0d4f0..202d364 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns, data = [], []
columns = get_columns()
@@ -149,4 +151,4 @@
"fieldtype": "Float",
"width": 110
}
- ]
\ No newline at end of file
+ ]
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 2da53d7..9299cca 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if filters.from_date >= filters.to_date:
frappe.msgprint(_("To Date must be greater than From Date"))
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 d8de701..7aeae45 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
@@ -3,12 +3,18 @@
# Compiled at: 2019-05-06 09:51:46
# Decompiled by https://python-decompiler.com
from __future__ import unicode_literals
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+
+import unittest
+
+import frappe
+
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_to_be_received import (
+ execute,
+)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_to_be_received import execute
-import frappe, unittest
-from pprint import pprint
+
class TestSubcontractedItemToBeReceived(unittest.TestCase):
@@ -33,4 +39,4 @@
pr.items[0].qty = quantity
pr.supplier_warehouse = '_Test Warehouse 1 - _TC'
pr.insert()
- pr.submit()
\ No newline at end of file
+ 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 68426ab..a8fad96 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if filters.from_date >= filters.to_date:
frappe.msgprint(_("To Date must be greater than From Date"))
@@ -94,4 +96,4 @@
["Purchase Order", "transaction_date", ">=", filters.from_date],
["Purchase Order", "docstatus", "=", 1]
]
- )
\ No newline at end of file
+ )
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 2448e17..dcdc5e3 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
@@ -3,11 +3,19 @@
# Compiled at: 2019-05-06 10:24:35
# Decompiled by https://python-decompiler.com
from __future__ import unicode_literals
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+
+import json
+import unittest
+
+import frappe
+
from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import (
+ execute,
+)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import execute
-import json, frappe, unittest
+
class TestSubcontractedItemToBeTransferred(unittest.TestCase):
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html
index 098214d..015b31c 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html
@@ -129,4 +129,4 @@
-<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
\ No newline at end of file
+<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
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 80e521a..7a8d08d 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
@@ -174,4 +174,4 @@
});
dialog.show();
}
-}
\ No newline at end of file
+}
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 2b37191..62b83ed 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
@@ -2,12 +2,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt, cint
-from frappe import _
+
from collections import defaultdict
+
+import frappe
+from frappe import _
+from frappe.utils import cint, flt
+
from erpnext.setup.utils import get_exchange_rate
+
def execute(filters=None):
if not filters:
return [], []
@@ -263,4 +267,4 @@
<span class="indicator red">
Expires today / Already Expired
- </span>"""
\ No newline at end of file
+ </span>"""
diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py
index a73cb0d..81d995c 100644
--- a/erpnext/buying/utils.py
+++ b/erpnext/buying/utils.py
@@ -2,13 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt, cstr, cint
-from frappe import _
+
import json
-from erpnext.stock.doctype.item.item import get_last_purchase_details
-from erpnext.stock.doctype.item.item import validate_end_of_life
+import frappe
+from frappe import _
+from frappe.utils import cint, cstr, flt
+
+from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
+
def update_last_purchase_rate(doc, is_submit):
"""updates last_purchase_rate in item table for each item"""
@@ -102,4 +104,3 @@
mr_list.append(material_request)
return mr_list
-
diff --git a/erpnext/change_log/v13/v13_9_0.md b/erpnext/change_log/v13/v13_9_0.md
new file mode 100644
index 0000000..e527666
--- /dev/null
+++ b/erpnext/change_log/v13/v13_9_0.md
@@ -0,0 +1,46 @@
+# Version 13.9.0 Release Notes
+
+### Features & Enhancements
+- Organizational Chart ([#26261](https://github.com/frappe/erpnext/pull/26261))
+- Enable discount accounting ([#26579](https://github.com/frappe/erpnext/pull/26579))
+- Added multi-select fields in promotional scheme to create multiple pricing rules ([#25622](https://github.com/frappe/erpnext/pull/25622))
+- Over transfer allowance for material transfers ([#26814](https://github.com/frappe/erpnext/pull/26814))
+- Enhancements in Tax Withholding Category ([#26661](https://github.com/frappe/erpnext/pull/26661))
+
+### Fixes
+- Sales Return cancellation if linked with Payment Entry ([#26883](https://github.com/frappe/erpnext/pull/26883))
+- Production plan not fetching sales order of a variant ([#25845](https://github.com/frappe/erpnext/pull/25845))
+- Stock Analytics Report must consider warehouse during calculation ([#26908](https://github.com/frappe/erpnext/pull/26908))
+- Incorrect date difference calculation ([#26805](https://github.com/frappe/erpnext/pull/26805))
+- Tax calculation for Recurring additional salary ([#24206](https://github.com/frappe/erpnext/pull/24206))
+- Cannot cancel payment entry if linked with invoices ([#26703](https://github.com/frappe/erpnext/pull/26703))
+- Included company in link document type filters for contact ([#26576](https://github.com/frappe/erpnext/pull/26576))
+- Fetch Payment Terms from linked Sales/Purchase Order ([#26723](https://github.com/frappe/erpnext/pull/26723))
+- Let all System Managers be able to delete Company transactions ([#26819](https://github.com/frappe/erpnext/pull/26819))
+- Bank remittance report issue ([#26398](https://github.com/frappe/erpnext/pull/26398))
+- Faulty Gl Entry for Asset LCVs ([#26803](https://github.com/frappe/erpnext/pull/26803))
+- Clean Serial No input on Server Side ([#26878](https://github.com/frappe/erpnext/pull/26878))
+- Supplier invoice importer fix v13 ([#26633](https://github.com/frappe/erpnext/pull/26633))
+- POS payment modes displayed wrong total ([#26808](https://github.com/frappe/erpnext/pull/26808))
+- Fetching of item tax from hsn code ([#26736](https://github.com/frappe/erpnext/pull/26736))
+- Cannot cancel invoice if IRN cancelled on portal ([#26879](https://github.com/frappe/erpnext/pull/26879))
+- Validate python expressions ([#26856](https://github.com/frappe/erpnext/pull/26856))
+- POS Item Cart non-stop scroll issue ([#26693](https://github.com/frappe/erpnext/pull/26693))
+- Add mandatory depends on condition for export type field ([#26958](https://github.com/frappe/erpnext/pull/26958))
+- Cannot generate IRNs for standalone credit notes ([#26824](https://github.com/frappe/erpnext/pull/26824))
+- Added progress bar in Repost Item Valuation to check the status of reposting ([#26630](https://github.com/frappe/erpnext/pull/26630))
+- TDS calculation for first threshold breach for TDS category 194Q ([#26710](https://github.com/frappe/erpnext/pull/26710))
+- Student category mapping from the program enrollment tool ([#26739](https://github.com/frappe/erpnext/pull/26739))
+- Cost center & account validation in Sales/Purchase Taxes and Charges ([#26881](https://github.com/frappe/erpnext/pull/26881))
+- Reset weight_per_unit on replacing Item ([#26791](https://github.com/frappe/erpnext/pull/26791))
+- Do not fetch fully return issued purchase receipts ([#26825](https://github.com/frappe/erpnext/pull/26825))
+- Incorrect amount in work order required items table. ([#26585](https://github.com/frappe/erpnext/pull/26585))
+- Additional discount calculations in Invoices ([#26553](https://github.com/frappe/erpnext/pull/26553))
+- Refactored Asset Repair ([#26415](https://github.com/frappe/erpnext/pull/25798))
+- Exchange rate revaluation posting date and precision fixes ([#26650](https://github.com/frappe/erpnext/pull/26650))
+- POS Invoice consolidated Sales Invoice field set to no copy ([#26768](https://github.com/frappe/erpnext/pull/26768))
+- Consider grand total for threshold check ([#26683](https://github.com/frappe/erpnext/pull/26683))
+- Budget variance missing values ([#26966](https://github.com/frappe/erpnext/pull/26966))
+- GL Entries for exchange gain loss ([#26728](https://github.com/frappe/erpnext/pull/26728))
+- Add missing cess amount in GSTR-3B report ([#26544](https://github.com/frappe/erpnext/pull/26544))
+- GST Reports timeout issue ([#26575](https://github.com/frappe/erpnext/pull/26575))
\ No newline at end of file
diff --git a/erpnext/commands/__init__.py b/erpnext/commands/__init__.py
index a991cf9..f3bf11a 100644
--- a/erpnext/commands/__init__.py
+++ b/erpnext/commands/__init__.py
@@ -1,10 +1,12 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
-from __future__ import unicode_literals, absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
+
import click
import frappe
-from frappe.commands import pass_context, get_site
+from frappe.commands import get_site, pass_context
+
def call_command(cmd, context):
return click.Context(cmd, obj=context).forward(cmd)
@@ -46,4 +48,4 @@
commands = [
make_demo
-]
\ No newline at end of file
+]
diff --git a/erpnext/communication/doctype/communication_medium/communication_medium.py b/erpnext/communication/doctype/communication_medium/communication_medium.py
index f233da0..b15c3be 100644
--- a/erpnext/communication/doctype/communication_medium/communication_medium.py
+++ b/erpnext/communication/doctype/communication_medium/communication_medium.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class CommunicationMedium(Document):
pass
diff --git a/erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.py b/erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.py
index d68d2d6..5189b09 100644
--- a/erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.py
+++ b/erpnext/communication/doctype/communication_medium_timeslot/communication_medium_timeslot.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class CommunicationMediumTimeslot(Document):
pass
diff --git a/erpnext/config/education.py b/erpnext/config/education.py
index 1c8ab10..ecd771f 100644
--- a/erpnext/config/education.py
+++ b/erpnext/config/education.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return [
{
diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py
index ab4db96..d4d4a72 100644
--- a/erpnext/config/projects.py
+++ b/erpnext/config/projects.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return [
{
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 4c79a5c..e9b531e 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2,27 +2,60 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
import json
+
+import frappe
from frappe import _, throw
-from frappe.utils import (today, flt, cint, fmt_money, formatdate,
- getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
-from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied, WorkflowPermissionError
-from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
-from erpnext.setup.utils import get_exchange_rate
-from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
-from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.buying.utils import update_last_purchase_rate
-from erpnext.controllers.sales_and_purchase_return import validate_return
-from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
-from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction,
- apply_pricing_rule_for_free_items, get_applied_pricing_rules)
-from erpnext.exceptions import InvalidCurrency
+from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ flt,
+ fmt_money,
+ formatdate,
+ get_last_day,
+ get_link_to_form,
+ getdate,
+ nowdate,
+ today,
+)
from six import text_type
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
-from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
+
+import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
+from erpnext.accounts.doctype.pricing_rule.utils import (
+ apply_pricing_rule_for_free_items,
+ apply_pricing_rule_on_transaction,
+ get_applied_pricing_rules,
+)
+from erpnext.accounts.party import (
+ get_party_account,
+ get_party_account_currency,
+ validate_party_frozen_disabled,
+)
+from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
+from erpnext.buying.utils import update_last_purchase_rate
+from erpnext.controllers.print_settings import (
+ set_print_templates_for_item_table,
+ set_print_templates_for_taxes,
+)
+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.packed_item.packed_item import make_packing_list
-from erpnext.controllers.print_settings import set_print_templates_for_item_table, set_print_templates_for_taxes
+from erpnext.stock.get_item_details import (
+ _get_item_tax_template,
+ get_conversion_factor,
+ get_item_details,
+ get_item_tax_map,
+ get_item_warehouse,
+)
+from erpnext.utilities.transaction_base import TransactionBase
+
class AccountMissingError(frappe.ValidationError): pass
@@ -135,14 +168,9 @@
validate_regional(self)
- validate_einvoice_fields(self)
-
if self.doctype != 'Material Request':
apply_pricing_rule_on_transaction(self)
- def before_cancel(self):
- validate_einvoice_fields(self)
-
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'):
@@ -164,7 +192,8 @@
self.set_due_date()
self.set_payment_schedule()
self.validate_payment_schedule_amount()
- self.validate_due_date()
+ if not self.get('ignore_default_payment_terms_template'):
+ self.validate_due_date()
self.validate_advance_entries()
def validate_non_invoice_documents_schedule(self):
@@ -656,13 +685,17 @@
.format(d.reference_name, d.against_order))
def set_advance_gain_or_loss(self):
- if not self.get("advances"):
+ if self.get('conversion_rate') == 1 or not self.get("advances"):
+ return
+
+ 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 != 1
- 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
@@ -681,7 +714,7 @@
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 {}")
+ 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:
@@ -700,7 +733,7 @@
"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,
+ "cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
"project": self.project
}, item=d)
)
@@ -842,7 +875,7 @@
dr_or_cr = "credit"
rev_dr_cr = "debit"
supplier_or_customer = self.supplier
-
+
else:
dr_or_cr = "debit"
rev_dr_cr = "credit"
@@ -853,11 +886,11 @@
discount_amount = item.discount_amount * item.qty
if self.doctype == "Purchase Invoice":
income_or_expense_account = (item.expense_account
- if (not item.enable_deferred_expense or self.is_return)
+ if (not item.enable_deferred_expense or self.is_return)
else item.deferred_expense_account)
else:
income_or_expense_account = (item.income_account
- if (not item.enable_deferred_revenue or self.is_return)
+ if (not item.enable_deferred_revenue or self.is_return)
else item.deferred_revenue_account)
account_currency = get_account_currency(item.discount_account)
@@ -866,7 +899,7 @@
"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'),
+ 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
@@ -879,7 +912,7 @@
"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'),
+ 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
@@ -894,8 +927,8 @@
dr_or_cr: self.discount_amount,
"cost_center": self.cost_center
}, item=self)
- )
-
+ )
+
def allocate_advance_taxes(self, gl_entries):
tax_map = self.get_tax_map()
for pe in self.get("advances"):
@@ -951,42 +984,55 @@
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')
+ user_roles = frappe.get_roles()
+
+ total_overbilled_amt = 0.0
+
for item in self.get("items"):
- if item.get(item_ref_dn):
- 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(
- _("Warning: System will not check overbilling since amount for Item {0} in {1} is zero")
- .format(item.item_code, ref_dt))
- else:
- already_billed = frappe.db.sql("""
- select sum(%s)
- from `tab%s`
- where %s=%s and docstatus=1 and parent != %s
- """ % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
- (item.get(item_ref_dn), self.name))[0][0]
+ if not item.get(item_ref_dn):
+ continue
- total_billed_amt = flt(flt(already_billed) + flt(item.get(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")
+ continue
- allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
- get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
+ already_billed = frappe.db.sql("""
+ select sum(%s)
+ from `tab%s`
+ where %s=%s and docstatus=1 and parent != %s
+ """ % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
+ (item.get(item_ref_dn), self.name))[0][0]
- max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
+ total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
+ self.precision(based_on, item))
- if total_billed_amt < 0 and max_allowed_amt < 0:
- # while making debit note against purchase return entry(purchase receipt) getting overbill error
- total_billed_amt = abs(total_billed_amt)
- max_allowed_amt = abs(max_allowed_amt)
+ allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
+ get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
- role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
+ max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
- if total_billed_amt - max_allowed_amt > 0.01 and role_allowed_to_over_bill not in frappe.get_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")):
- self.throw_overbill_exception(item, max_allowed_amt)
+ if total_billed_amt < 0 and max_allowed_amt < 0:
+ # while making debit note against purchase return entry(purchase receipt) getting overbill error
+ total_billed_amt = abs(total_billed_amt)
+ max_allowed_amt = abs(max_allowed_amt)
+
+ overbill_amt = total_billed_amt - max_allowed_amt
+ total_overbilled_amt += overbill_amt
+
+ 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")):
+ 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), title=_("Warning"), indicator="orange")
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")
@@ -1210,7 +1256,7 @@
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(base_grand_total * 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):
@@ -1223,7 +1269,7 @@
po_or_so = self.get('items')[0].get('purchase_order')
po_or_so_doctype = "Purchase Order"
po_or_so_doctype_name = "purchase_order"
-
+
return po_or_so, po_or_so_doctype, po_or_so_doctype_name
def linked_order_has_payment_terms(self, po_or_so, fieldname, doctype):
@@ -1232,14 +1278,14 @@
return True
elif self.linked_order_has_payment_schedule(po_or_so):
return True
-
+
return False
def all_items_have_same_po_or_so(self, po_or_so, fieldname):
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):
@@ -1367,6 +1413,67 @@
return False
+ def process_common_party_accounting(self):
+ 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'):
+ party_link = self.get_common_party_link()
+ if party_link and self.outstanding_amount:
+ self.create_advance_and_reconcile(party_link)
+
+ 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
+ )
+
+ def create_advance_and_reconcile(self, party_link):
+ secondary_party_type, secondary_party = self.get_party()
+ primary_party_type, primary_party = party_link.primary_role, party_link.primary_party
+
+ 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.posting_date = self.posting_date
+ jv.company = self.company
+ jv.remark = 'Adjustment for {} {}'.format(self.doctype, self.name)
+
+ reconcilation_entry = frappe._dict()
+ advance_entry = frappe._dict()
+
+ reconcilation_entry.account = secondary_account
+ reconcilation_entry.party_type = secondary_party_type
+ reconcilation_entry.party = secondary_party
+ reconcilation_entry.reference_type = self.doctype
+ reconcilation_entry.reference_name = self.name
+ reconcilation_entry.cost_center = self.cost_center
+
+ advance_entry.account = primary_account
+ advance_entry.party_type = primary_party_type
+ advance_entry.party = primary_party
+ advance_entry.cost_center = self.cost_center
+ advance_entry.is_advance = 'Yes'
+
+ 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.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)
@@ -1530,7 +1637,7 @@
def get_advance_payment_entries(party_type, party, party_account, order_doctype,
- order_list=None, include_unallocated=True, against_all_orders=False, limit=None):
+ 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"
payment_type = "Receive" if party_type == "Customer" else "Pay"
@@ -1565,27 +1672,31 @@
if include_unallocated:
unallocated_payment_entries = frappe.db.sql("""
- select "Payment Entry" as reference_type, name as reference_name,
- remarks, unallocated_amount as amount, {2} as exchange_rate
+ 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
+ and docstatus = 1 and unallocated_amount > 0 {condition}
order by posting_date {1}
- """.format(party_account_field, limit_cond, exchange_rate_field),
+ """.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():
- # Daily update the status of the invoices
+ """Updates status as Overdue for applicable invoices. Runs daily."""
- frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
- where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
-
- frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
- where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
-
+ for doctype in ("Sales Invoice", "Purchase Invoice"):
+ frappe.db.sql("""
+ update `tab{}` as dt set dt.status = 'Overdue'
+ where dt.docstatus = 1
+ and dt.status != 'Overdue'
+ and dt.outstanding_amount > 0
+ and (dt.grand_total - dt.outstanding_amount) <
+ (select sum(payment_amount) from `tabPayment Schedule` as ps
+ where ps.parent = dt.name and ps.due_date < %s)
+ """.format(doctype), getdate())
@frappe.whitelist()
def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
@@ -1754,7 +1865,12 @@
def update_bin_on_delete(row, doctype):
"""Update bin for deleted item (row)."""
- from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty, get_ordered_qty, get_indented_qty
+ from erpnext.stock.stock_balance import (
+ get_indented_qty,
+ get_ordered_qty,
+ get_reserved_qty,
+ update_bin_qty,
+ )
qty_dict = {}
if doctype == "Sales Order":
@@ -1841,6 +1957,11 @@
for d in data:
new_child_flag = False
+
+ if not d.get("item_code"):
+ # ignore empty rows
+ continue
+
if not d.get("docname"):
new_child_flag = True
check_doc_permissions(parent, 'create')
@@ -1863,7 +1984,7 @@
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 == new_date if prev_date and new_date else False # in case of delivery note etc
+ 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
@@ -1975,7 +2096,3 @@
@erpnext.allow_regional
def validate_regional(doc):
pass
-
-@erpnext.allow_regional
-def validate_einvoice_fields(doc):
- pass
\ No newline at end of file
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 974ade3..e0b3ad8 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -2,24 +2,21 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, msgprint
-from frappe.utils import flt,cint, cstr, getdate
-from six import iteritems
-from collections import OrderedDict
-from erpnext.accounts.party import get_party_details
-from erpnext.stock.get_item_details import get_conversion_factor
-from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
-from erpnext.stock.stock_ledger import get_valuation_rate
-from erpnext.stock.doctype.serial_no.serial_no import get_auto_serial_nos, auto_make_serial_nos, get_serial_nos
from frappe.contacts.doctype.address.address import get_address_display
+from frappe.utils import cint, cstr, flt, getdate
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
+from erpnext.accounts.party import get_party_details
+from erpnext.buying.utils import update_last_purchase_rate, validate_for_items
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
-from erpnext.stock.utils import get_incoming_rate
-
from erpnext.controllers.stock_controller import StockController
from erpnext.controllers.subcontracting import Subcontracting
+from erpnext.stock.get_item_details import get_conversion_factor
+from erpnext.stock.utils import get_incoming_rate
+
class BuyingController(StockController, Subcontracting):
diff --git a/erpnext/controllers/employee_boarding_controller.py b/erpnext/controllers/employee_boarding_controller.py
index 1898222..b8dc92e 100644
--- a/erpnext/controllers/employee_boarding_controller.py
+++ b/erpnext/controllers/employee_boarding_controller.py
@@ -5,7 +5,11 @@
from frappe import _
from frappe.desk.form import assign_to
from frappe.model.document import Document
-from frappe.utils import flt, unique
+from frappe.utils import add_days, flt, unique
+
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+
class EmployeeBoardingController(Document):
'''
@@ -41,10 +45,14 @@
def create_task_and_notify_user(self):
# create the task for the given project and assign to the concerned person
+ holiday_list = self.get_holiday_list()
+
for activity in self.activities:
if activity.task:
continue
+ dates = self.get_task_dates(activity, holiday_list)
+
task = frappe.get_doc({
'doctype': 'Task',
'project': self.project,
@@ -52,7 +60,9 @@
'description': activity.description,
'department': self.department,
'company': self.company,
- 'task_weight': activity.task_weight
+ '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)
@@ -79,6 +89,36 @@
if users:
self.assign_task_to_users(task, users)
+ def get_holiday_list(self):
+ 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)
+ else:
+ return self.holiday_list
+
+ def get_task_dates(self, activity, holiday_list):
+ start_date = end_date = None
+
+ if activity.begin_on:
+ start_date = add_days(self.boarding_begins_on, activity.begin_on)
+ start_date = self.update_if_holiday(start_date, holiday_list)
+
+ if activity.duration:
+ end_date = add_days(self.boarding_begins_on, activity.begin_on + activity.duration)
+ end_date = self.update_if_holiday(end_date, holiday_list)
+
+ return [start_date, end_date]
+
+ def update_if_holiday(self, date, holiday_list):
+ while is_holiday(holiday_list, date):
+ date = add_days(date, 1)
+ return date
+
def assign_task_to_users(self, task, users):
for user in users:
args = {
@@ -103,7 +143,8 @@
@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'],
+ fields=['activity_name', 'role', 'user', 'required_for_employee_creation',
+ 'description', 'task_weight', 'begin_on', 'duration'],
filters={'parent': parent, 'parenttype': parenttype},
order_by= 'idx')
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 051481f..1b56ae9 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -2,13 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
+import copy
+import json
+
import frappe
from frappe import _
from frappe.utils import cstr, flt
-import json, copy
-
from six import string_types
+
class ItemVariantExistsError(frappe.ValidationError): pass
class InvalidItemAttributeValueError(frappe.ValidationError): pass
class ItemTemplateCannotHaveStock(frappe.ValidationError): pass
@@ -344,4 +347,3 @@
variant.name = variant.item_code
validate_item_variant_attributes(variant, args)
return variant.as_dict()
-
diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py
index e08c400..f6e061b 100644
--- a/erpnext/controllers/print_settings.py
+++ b/erpnext/controllers/print_settings.py
@@ -2,8 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint
+
def set_print_templates_for_item_table(doc, settings):
doc.print_templates = {
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 21c052a..7b4566a 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -2,14 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-import erpnext
+
import json
-from frappe.desk.reportview import get_match_cond, get_filters_cond
-from frappe.utils import nowdate, getdate
from collections import defaultdict
+
+import frappe
+from frappe import scrub
+from frappe.desk.reportview import get_filters_cond, get_match_cond
+from frappe.utils import nowdate, unique
+
+import erpnext
from erpnext.stock.get_item_details import _get_item_tax_template
-from frappe.utils import unique
+
# searches for active employees
@frappe.whitelist()
@@ -220,18 +224,29 @@
if not field in searchfields]
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
- if filters and isinstance(filters, dict) and filters.get('supplier'):
- item_group_list = frappe.get_all('Supplier Item Group',
- filters = {'supplier': filters.get('supplier')}, fields = ['item_group'])
+ 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'])
- item_groups = []
- for i in item_group_list:
- item_groups.append(i.item_group)
+ filters_dict = {}
+ for rule in item_rules_list:
+ if rule['restrict_based_on'] == 'Item':
+ rule['restrict_based_on'] = 'name'
+ filters_dict[rule.restrict_based_on] = []
- del filters['supplier']
+ for rule in item_rules_list:
+ filters_dict[rule.restrict_based_on].append(rule.based_on_value)
- if item_groups:
- filters['item_group'] = ['in', item_groups]
+ for filter in filters_dict:
+ filters[scrub(filter)] = ['in', filters_dict[filter]]
+
+ if filters.get('customer'):
+ del filters['customer']
+ else:
+ del filters['supplier']
+
description_cond = ''
if frappe.db.count('Item', cache=True) < 50000:
@@ -304,7 +319,7 @@
@frappe.validate_and_sanitize_search_inputs
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
cond = ''
- if filters.get('customer'):
+ if filters and filters.get('customer'):
cond = """(`tabProject`.customer = %s or
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
@@ -517,7 +532,9 @@
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters):
- from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map
+ 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')))
query_filters = []
@@ -526,6 +543,9 @@
if meta.is_tree:
query_filters.append(['is_group', '=', 0])
+ if meta.has_field('disabled'):
+ query_filters.append(['disabled', '!=', 1])
+
if meta.has_field('company'):
query_filters.append(['company', '=', filters.get('company')])
@@ -678,34 +698,6 @@
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
-def get_healthcare_service_units(doctype, txt, searchfield, start, page_len, filters):
- query = """
- select name
- from `tabHealthcare Service Unit`
- where
- is_group = 0
- and company = {company}
- and name like {txt}""".format(
- company = frappe.db.escape(filters.get('company')), txt = frappe.db.escape('%{0}%'.format(txt)))
-
- if filters and filters.get('inpatient_record'):
- from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
- service_unit = get_current_healthcare_service_unit(filters.get('inpatient_record'))
-
- # if the patient is admitted, then appointments should be allowed against the admission service unit,
- # inspite of it being an Inpatient Occupancy service unit
- if service_unit:
- query += " and (allow_appointments = 1 or name = {service_unit})".format(service_unit = frappe.db.escape(service_unit))
- else:
- query += " and allow_appointments = 1"
- else:
- query += " and allow_appointments = 1"
-
- return frappe.db.sql(query, filters)
-
-
-@frappe.whitelist()
-@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'))
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 80ccc6d..5f2fbeb 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -2,11 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
from frappe.model.meta import get_field_precision
+from frappe.utils import flt, format_datetime, get_datetime
+
+import erpnext
from erpnext.stock.utils import get_incoming_rate
-from frappe.utils import flt, get_datetime, format_datetime
+
class StockOverReturnError(frappe.ValidationError): pass
@@ -63,7 +67,7 @@
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""".format(doc.doctype), doc.return_against, as_dict=1):
+ 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)
@@ -235,6 +239,7 @@
def make_return_doc(doctype, source_name, 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")
@@ -329,7 +334,6 @@
target_doc.po_detail = source_doc.po_detail
target_doc.pr_detail = source_doc.pr_detail
target_doc.purchase_invoice_item = source_doc.name
- target_doc.price_list_rate = 0
elif doctype == "Delivery Note":
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
@@ -360,7 +364,6 @@
else:
target_doc.pos_invoice_item = source_doc.name
- target_doc.price_list_rate = 0
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
@@ -396,19 +399,6 @@
if not return_against:
return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against")
- if not return_against and voucher_type == 'Sales Invoice' and sle:
- return 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'),
- "company": sle.company,
- "voucher_type": sle.voucher_type,
- "voucher_no": sle.voucher_no
- }, raise_error_if_no_rate=False)
-
return_against_item_field = get_return_against_item_fields(voucher_type)
filters = get_filters(voucher_type, voucher_no, voucher_detail_no,
@@ -419,7 +409,24 @@
else:
select_field = "abs(stock_value_difference / actual_qty)"
- return flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
+ 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 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'),
+ "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 = {
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index da2765d..bb269f3 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -2,21 +2,21 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint, flt, cstr, get_link_to_form, nowtime
-from frappe import _, throw
-from erpnext.stock.get_item_details import get_bin_details
-from erpnext.stock.utils import get_incoming_rate
-from erpnext.stock.get_item_details import get_conversion_factor
-from erpnext.stock.doctype.item.item import set_item_default
-from frappe.contacts.doctype.address.address import get_address_display
-from erpnext.controllers.accounts_controller import get_taxes_and_charges
-from erpnext.controllers.stock_controller import StockController
+import frappe
+from frappe import _, bold, throw
+from frappe.contacts.doctype.address.address import get_address_display
+from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime
+
+from erpnext.controllers.accounts_controller import get_taxes_and_charges
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
+from erpnext.controllers.stock_controller import StockController
+from erpnext.stock.doctype.item.item import set_item_default
+from erpnext.stock.get_item_details import get_bin_details, get_conversion_factor
+from erpnext.stock.utils import get_incoming_rate
+
class SellingController(StockController):
-
def get_feed(self):
return _("To {0} | {1} {2}").format(self.customer_name, self.currency,
self.grand_total)
@@ -169,39 +169,96 @@
def validate_selling_price(self):
def throw_message(idx, item_name, rate, ref_rate_field):
- bold_net_rate = frappe.bold("net rate")
- msg = (_("""Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {}""")
- .format(idx, frappe.bold(item_name), frappe.bold(ref_rate_field), bold_net_rate, frappe.bold(rate)))
- msg += "<br><br>"
- msg += (_("""You can alternatively disable selling price validation in {} to bypass this validation.""")
- .format(get_link_to_form("Selling Settings", "Selling Settings")))
- frappe.throw(msg, title=_("Invalid Selling Price"))
+ 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"))
- if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
- return
- if hasattr(self, "is_return") and self.is_return:
+ if (
+ self.get("is_return")
+ or not frappe.db.get_single_value("Selling Settings", "validate_selling_price")
+ ):
return
- for it in self.get("items"):
- if not it.item_code:
+ is_internal_customer = self.get('is_internal_customer')
+ valuation_rate_map = {}
+
+ for item in self.items:
+ if not item.item_code:
continue
- last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
- last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1)
- if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
- throw_message(it.idx, frappe.bold(it.item_name), last_purchase_rate_in_sales_uom, "last purchase rate")
+ last_purchase_rate, is_stock_item = frappe.get_cached_value(
+ "Item", item.item_code, ("last_purchase_rate", "is_stock_item")
+ )
- last_valuation_rate = frappe.db.sql("""
- SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s
- AND warehouse = %s AND valuation_rate > 0
- ORDER BY posting_date DESC, posting_time DESC, creation DESC LIMIT 1
- """, (it.item_code, it.warehouse))
- if last_valuation_rate:
- last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] * (it.conversion_factor or 1)
- if is_stock_item and flt(it.base_net_rate) < flt(last_valuation_rate_in_sales_uom) \
- and not self.get('is_internal_customer'):
- throw_message(it.idx, frappe.bold(it.item_name), last_valuation_rate_in_sales_uom, "valuation rate")
+ 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"
+ )
+
+ if is_internal_customer or not is_stock_item:
+ continue
+
+ valuation_rate_map[(item.item_code, item.warehouse)] = None
+
+ if not valuation_rate_map:
+ return
+
+ or_conditions = (
+ f"""(item_code = {frappe.db.escape(valuation_rate[0])}
+ and warehouse = {frappe.db.escape(valuation_rate[1])})"""
+ for valuation_rate in valuation_rate_map
+ )
+
+ valuation_rates = frappe.db.sql(f"""
+ select
+ item_code, warehouse, valuation_rate
+ from
+ `tabBin`
+ where
+ ({" or ".join(or_conditions)})
+ and valuation_rate > 0
+ """, as_dict=True)
+
+ for rate in valuation_rates:
+ valuation_rate_map[(rate.item_code, rate.warehouse)] = rate.valuation_rate
+
+ for item in self.items:
+ if not item.item_code:
+ continue
+
+ 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)
+ )
+
+ 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"
+ )
def get_item_list(self):
il = []
@@ -306,7 +363,7 @@
sales_order.update_reserved_qty(so_item_rows)
def set_incoming_rate(self):
- if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
+ if self.doctype not in ("Delivery Note", "Sales Invoice"):
return
items = self.get("items") + (self.get("packed_items") or [])
@@ -315,18 +372,19 @@
# Get incoming rate based on original item cost based on valuation method
qty = flt(d.get('stock_qty') or d.get('actual_qty'))
- 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'),
- "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)
+ if not 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'),
+ "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():
@@ -501,6 +559,12 @@
frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
.format(d.idx, warehouse, warehouse))
+ if not self.get("is_internal_customer") and any(d.get("target_warehouse") for d in items):
+ msg = _("Target Warehouse is set for some items but the customer is not an internal customer.")
+ msg += " " + _("This {} will be treated as material transfer.").format(_(self.doctype))
+ frappe.msgprint(msg, title="Internal Transfer", alert=True)
+
+
def validate_items(self):
# validate items to see if they have is_sales_item enabled
from erpnext.controllers.buying_controller import validate_item_type
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 943f7aa..8738204 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt, comma_or, nowdate, getdate
from frappe import _
from frappe.model.document import Document
+from frappe.utils import comma_or, flt, getdate, now, nowdate
+
class OverAllowanceError(frappe.ValidationError): pass
@@ -86,7 +88,8 @@
],
"Bank Transaction": [
["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"],
- ["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"]
+ ["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"],
+ ["Cancelled", "eval:self.docstatus == 2"]
],
"POS Opening Entry": [
["Draft", None],
@@ -336,10 +339,14 @@
target.notify_update()
def _update_modified(self, args, update_modified):
- args['update_modified'] = ''
- if update_modified:
- args['update_modified'] = ', modified = now(), modified_by = {0}'\
- .format(frappe.db.escape(frappe.session.user))
+ if not 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)
+ )
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
ref_fieldname = frappe.scrub(ref_dt)
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 17707ec..78a6e52 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -5,12 +5,15 @@
from collections import defaultdict
import frappe
-import frappe.defaults
from frappe import _
from frappe.utils import cint, cstr, flt, get_link_to_form, getdate
import erpnext
-from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
+from erpnext.accounts.general_ledger import (
+ make_gl_entries,
+ make_reverse_gl_entries,
+ process_gl_map,
+)
from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock import get_warehouse_account_map
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
index 36ae110..3addb91 100644
--- a/erpnext/controllers/subcontracting.py
+++ b/erpnext/controllers/subcontracting.py
@@ -1,10 +1,13 @@
-import frappe
import copy
-from frappe import _
-from frappe.utils import flt, cint, get_link_to_form
from collections import defaultdict
+
+import frappe
+from frappe import _
+from frappe.utils import cint, flt, get_link_to_form
+
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
class Subcontracting():
def set_materials_for_subcontracted_items(self, raw_material_table):
if self.doctype == 'Purchase Invoice' and not self.update_stock:
@@ -390,4 +393,4 @@
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}'
- frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed"))
\ No newline at end of file
+ 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 05edb25..70cc8a5 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -2,15 +2,23 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import json
-import frappe, erpnext
+
+import frappe
from frappe import _, scrub
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
-from erpnext.controllers.accounts_controller import validate_conversion_rate, \
- validate_taxes_and_charges, validate_inclusive_tax
-from erpnext.stock.get_item_details import _get_item_tax_template
-from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
+
+import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
+from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
+from erpnext.controllers.accounts_controller import (
+ validate_conversion_rate,
+ validate_inclusive_tax,
+ validate_taxes_and_charges,
+)
+from erpnext.stock.get_item_details import _get_item_tax_template
+
class calculate_taxes_and_totals(object):
def __init__(self, doc):
@@ -595,7 +603,8 @@
self.doc.precision("outstanding_amount"))
if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
- self.update_paid_amount_for_return(total_amount_to_pay)
+ self.set_total_amount_to_default_mop(total_amount_to_pay)
+ self.calculate_paid_amount()
def calculate_paid_amount(self):
@@ -675,7 +684,7 @@
def set_item_wise_tax_breakup(self):
self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
- def update_paid_amount_for_return(self, total_amount_to_pay):
+ 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)
@@ -687,8 +696,6 @@
'default': 1
})
- self.calculate_paid_amount()
-
def get_itemised_tax_breakup_html(doc):
if not doc.taxes:
return
diff --git a/erpnext/controllers/tests/test_item_variant.py b/erpnext/controllers/tests/test_item_variant.py
index 813f0a0..b3633e6 100644
--- a/erpnext/controllers/tests/test_item_variant.py
+++ b/erpnext/controllers/tests/test_item_variant.py
@@ -1,15 +1,18 @@
from __future__ import unicode_literals
-import frappe
import json
import unittest
-from erpnext.stock.doctype.item.test_item import set_item_variant_settings
-from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
-from erpnext.stock.doctype.quality_inspection.test_quality_inspection import create_quality_inspection_parameter
-
+import frappe
from six import string_types
+from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
+from erpnext.stock.doctype.item.test_item import set_item_variant_settings
+from erpnext.stock.doctype.quality_inspection.test_quality_inspection import (
+ create_quality_inspection_parameter,
+)
+
+
class TestItemVariant(unittest.TestCase):
def test_tables_in_template_copied_to_variant(self):
fields = [{'field_name': 'quality_inspection_template'}]
diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py
index 7a4b2d3..2d1ae43 100644
--- a/erpnext/controllers/tests/test_mapper.py
+++ b/erpnext/controllers/tests/test_mapper.py
@@ -1,12 +1,14 @@
from __future__ import unicode_literals
-import unittest
-import frappe
-import random, json
+import json
+import unittest
+
+import frappe
import frappe.utils
-from frappe.utils import nowdate, add_months
from frappe.model import mapper
from frappe.test_runner import make_test_records
+from frappe.utils import add_months, nowdate
+
class TestMapper(unittest.TestCase):
def test_map_docs(self):
diff --git a/erpnext/controllers/tests/test_qty_based_taxes.py b/erpnext/controllers/tests/test_qty_based_taxes.py
index aaeac5d..41673d1 100644
--- a/erpnext/controllers/tests/test_qty_based_taxes.py
+++ b/erpnext/controllers/tests/test_qty_based_taxes.py
@@ -1,8 +1,11 @@
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
+
import unittest
-import frappe
from uuid import uuid4 as _uuid4
+import frappe
+
+
def uuid4():
return str(_uuid4())
diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py
new file mode 100644
index 0000000..05541d1
--- /dev/null
+++ b/erpnext/controllers/tests/test_queries.py
@@ -0,0 +1,87 @@
+import unittest
+from functools import partial
+
+from erpnext.controllers import queries
+
+
+def add_default_params(func, doctype):
+ return partial(
+ func, doctype=doctype, txt="", searchfield="name", start=0, page_len=20, filters=None
+ )
+
+
+class TestQueries(unittest.TestCase):
+
+ # All tests are based on doctype/test_records.json
+
+ def assert_nested_in(self, item, container):
+ self.assertIn(item, [vals for tuples in container for vals in tuples])
+
+ def test_employee_query(self):
+ query = add_default_params(queries.employee_query, "Employee")
+
+ self.assertGreaterEqual(len(query(txt="_Test Employee")), 3)
+ self.assertGreaterEqual(len(query(txt="_Test Employee 1")), 1)
+
+ def test_lead_query(self):
+ query = add_default_params(queries.lead_query, "Lead")
+
+ self.assertGreaterEqual(len(query(txt="_Test Lead")), 4)
+ self.assertEqual(len(query(txt="_Test Lead 4")), 1)
+
+ def test_customer_query(self):
+ query = add_default_params(queries.customer_query, "Customer")
+
+ self.assertGreaterEqual(len(query(txt="_Test Customer")), 7)
+ self.assertGreaterEqual(len(query(txt="_Test Customer USD")), 1)
+
+ def test_supplier_query(self):
+ query = add_default_params(queries.supplier_query, "Supplier")
+
+ self.assertGreaterEqual(len(query(txt="_Test Supplier")), 7)
+ self.assertGreaterEqual(len(query(txt="_Test Supplier USD")), 1)
+
+ def test_item_query(self):
+ query = add_default_params(queries.item_query, "Item")
+
+ self.assertGreaterEqual(len(query(txt="_Test Item")), 7)
+ self.assertEqual(len(query(txt="_Test Item Home Desktop 100 3")), 1)
+
+ fg_item = "_Test FG Item"
+ stock_items = query(txt=fg_item, filters={"is_stock_item": 1})
+ self.assert_nested_in("_Test FG Item", stock_items)
+
+ bundled_stock_items = query(txt="_test product bundle item 5", filters={"is_stock_item": 1})
+ self.assertEqual(len(bundled_stock_items), 0)
+
+ def test_bom_qury(self):
+ query = add_default_params(queries.bom, "BOM")
+
+ self.assertGreaterEqual(len(query(txt="_Test Item Home Desktop Manufactured")), 1)
+
+ def test_project_query(self):
+ query = add_default_params(queries.get_project_name, "BOM")
+
+ self.assertGreaterEqual(len(query(txt="_Test Project")), 1)
+
+ def test_account_query(self):
+ query = add_default_params(queries.get_account_list, "Account")
+
+ debtor_accounts = query(txt="Debtors", filters={"company": "_Test Company"})
+ self.assert_nested_in("Debtors - _TC", debtor_accounts)
+
+ def test_income_account_query(self):
+ query = add_default_params(queries.get_income_account, "Account")
+
+ self.assertGreaterEqual(len(query(filters={"company": "_Test Company"})), 1)
+
+ def test_expense_account_query(self):
+ query = add_default_params(queries.get_expense_account, "Account")
+
+ self.assertGreaterEqual(len(query(filters={"company": "_Test Company"})), 1)
+
+ def test_warehouse_query(self):
+ query = add_default_params(queries.warehouse_query, "Account")
+
+ wh = query(filters=[["Bin", "item_code", "=", "_Test Item"]])
+ self.assertGreaterEqual(len(wh), 1)
diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py
index 9b4b0eb..05d900d 100644
--- a/erpnext/controllers/trends.py
+++ b/erpnext/controllers/trends.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import getdate
from frappe import _
+from frappe.utils import getdate
+
def get_columns(filters, trans):
validate_filters(filters)
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index 7c072e4..8e5952c 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -2,12 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import json
+
import frappe
from frappe import _
+from frappe.modules.utils import get_module_app
from frappe.utils import flt, has_common
from frappe.utils.user import is_website_user
+
def get_list_context(context=None):
return {
"global_number_format": frappe.db.get_default("number_format") or "#,###.##",
@@ -18,8 +22,32 @@
"get_list": get_transaction_list
}
+def get_webform_list_context(module):
+ if get_module_app(module) != 'erpnext':
+ return
+ return {
+ "get_list": get_webform_transaction_list
+ }
-def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
+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:
+ filters = []
+
+ meta = frappe.get_meta(doctype)
+
+ for d in meta.fields:
+ 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))
+
+ 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):
user = frappe.session.user
ignore_permissions = False
@@ -43,7 +71,7 @@
filters.append(('customer', 'in', customers))
elif suppliers:
filters.append(('supplier', 'in', suppliers))
- else:
+ elif not custom:
return []
if doctype == 'Request for Quotation':
@@ -53,9 +81,16 @@
# Since customers and supplier do not have direct access to internal doctypes
ignore_permissions = True
+ if not customers and not suppliers and custom:
+ 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')
+ if custom:
+ return transactions
+
return post_process(doctype, transactions)
def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
index df73f09..f205534 100644
--- a/erpnext/crm/doctype/appointment/appointment.py
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -4,15 +4,13 @@
from __future__ import unicode_literals
-import urllib
from collections import Counter
-from datetime import timedelta
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_url, getdate
-from frappe.utils.verified_command import verify_request, get_signed_params
+from frappe.utils.verified_command import get_signed_params
class Appointment(Document):
@@ -235,4 +233,3 @@
# frappe.db.exists returns a tuple of a tuple
return frappe.get_doc('Employee', employee_docname[0][0])
return None
-
diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py
index c7563e9..59138a9 100644
--- a/erpnext/crm/doctype/appointment/test_appointment.py
+++ b/erpnext/crm/doctype/appointment/test_appointment.py
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
import datetime
+import unittest
+
+import frappe
def create_test_lead():
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
index dc3ae8b..0c64eb8 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
@@ -7,4 +7,4 @@
frappe.throw(__('In row {0} of Appointment Booking Slots: "To Time" must be later than "From Time".', [i + 1]));
}
});
-}
\ No newline at end of file
+}
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 27f14b1..7e1da67 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import datetime
+
import frappe
from frappe import _
-import datetime
from frappe.model.document import Document
@@ -28,10 +30,10 @@
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)
+ 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):
+ 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)
frappe.throw(_(err_msg))
diff --git a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
index 3dc3c39..5c5432c 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
+++ b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestAppointmentBookingSettings(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
index 3cadbc9..4741c8a 100644
--- a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
+++ b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AppointmentBookingSlots(Document):
pass
diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
index 8258471..e33d87b 100644
--- a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
+++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AvailabilityOfSlots(Document):
pass
diff --git a/erpnext/crm/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py
index e32799f..8b62800 100644
--- a/erpnext/crm/doctype/campaign/campaign.py
+++ b/erpnext/crm/doctype/campaign/campaign.py
@@ -5,6 +5,7 @@
from frappe.model.document import Document
from frappe.model.naming import set_name_by_naming_series
+
class Campaign(Document):
def autoname(self):
if frappe.defaults.get_global_default('campaign_naming_by') != 'Naming Series':
diff --git a/erpnext/crm/doctype/campaign/test_campaign.py b/erpnext/crm/doctype/campaign/test_campaign.py
index 7124b8c..2e25eb6 100644
--- a/erpnext/crm/doctype/campaign/test_campaign.py
+++ b/erpnext/crm/doctype/campaign/test_campaign.py
@@ -4,5 +4,6 @@
# import frappe
import unittest
+
class TestCampaign(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
index 8445b8a..f053e6e 100644
--- a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
+++ b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class CampaignEmailSchedule(Document):
pass
diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js
index 9968855..7848de7 100644
--- a/erpnext/crm/doctype/contract/contract.js
+++ b/erpnext/crm/doctype/contract/contract.js
@@ -15,7 +15,7 @@
let contract_template = r.message.contract_template;
frm.set_value("contract_terms", r.message.contract_terms);
frm.set_value("requires_fulfilment", contract_template.requires_fulfilment);
-
+
if (frm.doc.requires_fulfilment) {
// Populate the fulfilment terms table from a contract template, if any
r.message.contract_template.fulfilment_terms.forEach(element => {
@@ -23,7 +23,7 @@
d.requirement = element.requirement;
});
frm.refresh_field("fulfilment_terms");
- }
+ }
}
}
});
diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py
index c39397b..9654613 100644
--- a/erpnext/crm/doctype/contract/contract.py
+++ b/erpnext/crm/doctype/contract/contract.py
@@ -7,7 +7,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import getdate, now_datetime, nowdate
+from frappe.utils import getdate, nowdate
class Contract(Document):
diff --git a/erpnext/crm/doctype/contract/contract_list.js b/erpnext/crm/doctype/contract/contract_list.js
index 26a2907..7d56096 100644
--- a/erpnext/crm/doctype/contract/contract_list.js
+++ b/erpnext/crm/doctype/contract/contract_list.js
@@ -9,4 +9,4 @@
return [__(doc.status), "gray", "status,=," + doc.status];
}
},
-};
\ No newline at end of file
+};
diff --git a/erpnext/crm/doctype/contract/test_contract.js b/erpnext/crm/doctype/contract/test_contract.js
deleted file mode 100644
index 4c77c3d..0000000
--- a/erpnext/crm/doctype/contract/test_contract.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Contract", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Contract
- () => frappe.tests.make('Contract', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/crm/doctype/contract/test_contract.py b/erpnext/crm/doctype/contract/test_contract.py
index d5f4e71..8771636 100644
--- a/erpnext/crm/doctype/contract/test_contract.py
+++ b/erpnext/crm/doctype/contract/test_contract.py
@@ -8,6 +8,7 @@
import frappe
from frappe.utils import add_days, nowdate
+
class TestContract(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.py b/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.py
index 6039035..ae56f93 100644
--- a/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.py
+++ b/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ContractFulfilmentChecklist(Document):
pass
diff --git a/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.js b/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.js
deleted file mode 100644
index 2a2d5e1..0000000
--- a/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Contract Fulfilment Checklist", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Contract Fulfilment Checklist
- () => frappe.tests.make('Contract Fulfilment Checklist', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.py b/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.py
index c78796b..82e7ad3 100644
--- a/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.py
+++ b/erpnext/crm/doctype/contract_fulfilment_checklist/test_contract_fulfilment_checklist.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestContractFulfilmentChecklist(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py
index 69fd86f..fc1845c 100644
--- a/erpnext/crm/doctype/contract_template/contract_template.py
+++ b/erpnext/crm/doctype/contract_template/contract_template.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import json
+
import frappe
from frappe.model.document import Document
from frappe.utils.jinja import validate_template
from six import string_types
-import json
+
class ContractTemplate(Document):
def validate(self):
@@ -24,8 +27,8 @@
if contract_template.contract_terms:
contract_terms = frappe.render_template(contract_template.contract_terms, doc)
-
+
return {
- 'contract_template': contract_template,
+ 'contract_template': contract_template,
'contract_terms': contract_terms
- }
\ No newline at end of file
+ }
diff --git a/erpnext/crm/doctype/contract_template/test_contract_template.js b/erpnext/crm/doctype/contract_template/test_contract_template.js
deleted file mode 100644
index 6aaddd7..0000000
--- a/erpnext/crm/doctype/contract_template/test_contract_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Contract Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Contract Template
- () => frappe.tests.make('Contract Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/crm/doctype/contract_template/test_contract_template.py b/erpnext/crm/doctype/contract_template/test_contract_template.py
index b2b0db6..5065505 100644
--- a/erpnext/crm/doctype/contract_template/test_contract_template.py
+++ b/erpnext/crm/doctype/contract_template/test_contract_template.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestContractTemplate(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/contract_template_fulfilment_terms/contract_template_fulfilment_terms.py b/erpnext/crm/doctype/contract_template_fulfilment_terms/contract_template_fulfilment_terms.py
index 767b190..28f844d 100644
--- a/erpnext/crm/doctype/contract_template_fulfilment_terms/contract_template_fulfilment_terms.py
+++ b/erpnext/crm/doctype/contract_template_fulfilment_terms/contract_template_fulfilment_terms.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ContractTemplateFulfilmentTerms(Document):
pass
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index 71c93e8..4b74f25 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate, add_days, today, nowdate, cstr
-from frappe.model.document import Document
from frappe.core.doctype.communication.email import make
+from frappe.model.document import Document
+from frappe.utils import add_days, getdate, today
+
class EmailCampaign(Document):
def validate(self):
diff --git a/erpnext/crm/doctype/email_campaign/test_email_campaign.py b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
index f5eab48..f68b8c6 100644
--- a/erpnext/crm/doctype/email_campaign/test_email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestEmailCampaign(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 75af937..999599c 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -39,6 +39,8 @@
this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create"));
this.frm.add_custom_button(__("Opportunity"), this.make_opportunity, __("Create"));
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create"));
+ this.frm.add_custom_button(__("Prospect"), this.make_prospect, __("Create"));
+ this.frm.add_custom_button(__('Add to Prospect'), this.add_lead_to_prospect, __('Action'));
}
if (!this.frm.is_new()) {
@@ -49,6 +51,34 @@
}
}
+ add_lead_to_prospect () {
+ frappe.prompt([
+ {
+ fieldname: 'prospect',
+ label: __('Prospect'),
+ fieldtype: 'Link',
+ options: 'Prospect',
+ reqd: 1
+ }
+ ],
+ function(data) {
+ frappe.call({
+ method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect',
+ args: {
+ 'lead': cur_frm.doc.name,
+ 'prospect': data.prospect
+ },
+ callback: function(r) {
+ if (!r.exc) {
+ frm.reload_doc();
+ }
+ },
+ freeze: true,
+ freeze_message: __('...Adding Lead to Prospect')
+ });
+ }, __('Add Lead to Prospect'), __('Add'));
+ }
+
make_customer () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer",
@@ -70,6 +100,25 @@
})
}
+ make_prospect () {
+ frappe.model.with_doctype("Prospect", function() {
+ let prospect = frappe.model.get_new_doc("Prospect");
+ prospect.company_name = cur_frm.doc.company_name;
+ prospect.no_of_employees = cur_frm.doc.no_of_employees;
+ prospect.industry = cur_frm.doc.industry;
+ prospect.market_segment = cur_frm.doc.market_segment;
+ prospect.territory = cur_frm.doc.territory;
+ prospect.fax = cur_frm.doc.fax;
+ prospect.website = cur_frm.doc.website;
+ prospect.prospect_owner = cur_frm.doc.lead_owner;
+
+ let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead');
+ lead_prospect_row.lead = cur_frm.doc.name;
+
+ frappe.set_route("Form", "Prospect", prospect.name);
+ });
+ }
+
company_name () {
if (!this.frm.doc.lead_name) {
this.frm.set_value("lead_name", this.frm.doc.company_name);
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index 7f028cb..5cf110f 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -4,13 +4,23 @@
from __future__ import unicode_literals
import frappe
-from erpnext.accounts.party import set_taxes
-from erpnext.controllers.selling_controller import SellingController
from frappe import _
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
-from frappe.utils import cint, comma_and, cstr, getdate, has_gravatar, nowdate, validate_email_address
+from frappe.utils import (
+ cint,
+ comma_and,
+ cstr,
+ getdate,
+ has_gravatar,
+ nowdate,
+ validate_email_address,
+)
+
+from erpnext.accounts.party import set_taxes
+from erpnext.controllers.selling_controller import SellingController
+
class Lead(SellingController):
def get_feed(self):
@@ -34,9 +44,10 @@
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None,
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None,
})
-
+
def set_full_name(self):
- self.lead_name = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
+ if self.first_name:
+ self.lead_name = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
def validate_email_id(self):
if self.email_id:
@@ -62,6 +73,7 @@
def on_update(self):
self.add_calendar_event()
+ self.update_prospects()
def before_insert(self):
self.contact_doc = self.create_contact()
@@ -88,6 +100,12 @@
"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'])
+ for row in prospects:
+ 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
@@ -166,6 +184,7 @@
"salutation": self.salutation,
"gender": self.gender,
"designation": self.designation,
+ "company_name": self.company_name,
})
if self.email_id:
@@ -353,3 +372,13 @@
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.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')
diff --git a/erpnext/crm/doctype/lead/lead_dashboard.py b/erpnext/crm/doctype/lead/lead_dashboard.py
index 69d8ca7..5edf2b6 100644
--- a/erpnext/crm/doctype/lead/lead_dashboard.py
+++ b/erpnext/crm/doctype/lead/lead_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -13,7 +13,7 @@
},
'transactions': [
{
- 'items': ['Opportunity', 'Quotation']
+ 'items': ['Opportunity', 'Quotation', 'Prospect']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/crm/doctype/lead/lead_list.js b/erpnext/crm/doctype/lead/lead_list.js
new file mode 100644
index 0000000..75208fa
--- /dev/null
+++ b/erpnext/crm/doctype/lead/lead_list.js
@@ -0,0 +1,28 @@
+frappe.listview_settings['Lead'] = {
+ onload: function(listview) {
+ if (frappe.boot.user.can_create.includes("Prospect")) {
+ listview.page.add_action_item(__("Create Prospect"), function() {
+ frappe.model.with_doctype("Prospect", function() {
+ let prospect = frappe.model.get_new_doc("Prospect");
+ let leads = listview.get_checked_items();
+ frappe.db.get_value("Lead", leads[0].name, ["company_name", "no_of_employees", "industry", "market_segment", "territory", "fax", "website", "lead_owner"], (r) => {
+ prospect.company_name = r.company_name;
+ prospect.no_of_employees = r.no_of_employees;
+ prospect.industry = r.industry;
+ prospect.market_segment = r.market_segment;
+ prospect.territory = r.territory;
+ prospect.fax = r.fax;
+ prospect.website = r.website;
+ prospect.prospect_owner = r.lead_owner;
+
+ leads.forEach(function(lead) {
+ let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead');
+ lead_prospect_row.lead = lead.name;
+ });
+ frappe.set_route("Form", "Prospect", prospect.name);
+ });
+ });
+ });
+ }
+ }
+};
diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py
index d4886d3..833c43e 100644
--- a/erpnext/crm/doctype/lead/test_lead.py
+++ b/erpnext/crm/doctype/lead/test_lead.py
@@ -3,9 +3,10 @@
from __future__ import unicode_literals
+import unittest
+
import frappe
from frappe.utils import random_string
-import unittest
test_records = frappe.get_test_records('Lead')
@@ -82,4 +83,4 @@
"email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)),
}).insert()
- return lead_doc
\ No newline at end of file
+ return lead_doc
diff --git a/erpnext/crm/doctype/lead_source/lead_source.py b/erpnext/crm/doctype/lead_source/lead_source.py
index 5c64fb8..8de1c43 100644
--- a/erpnext/crm/doctype/lead_source/lead_source.py
+++ b/erpnext/crm/doctype/lead_source/lead_source.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class LeadSource(Document):
pass
diff --git a/erpnext/crm/doctype/lead_source/test_lead_source.py b/erpnext/crm/doctype/lead_source/test_lead_source.py
index b5bc649..ecf6117 100644
--- a/erpnext/crm/doctype/lead_source/test_lead_source.py
+++ b/erpnext/crm/doctype/lead_source/test_lead_source.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLeadSource(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
index 263005e..7aa0b77 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js
@@ -2,8 +2,8 @@
// For license information, please see license.txt
frappe.ui.form.on('LinkedIn Settings', {
- onload: function(frm){
- if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret){
+ onload: function(frm) {
+ if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret) {
frappe.confirm(
__('Session not valid, Do you want to login?'),
function(){
@@ -14,8 +14,9 @@
}
);
}
+ frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/linkedin-settings'>${__('Click here')}</a>`]));
},
- refresh: function(frm){
+ refresh: function(frm) {
if (frm.doc.session_status=="Expired"){
let msg = __("Session Not Active. Save doc to login.");
frm.dashboard.set_headline_alert(
@@ -53,7 +54,7 @@
);
}
},
- login: function(frm){
+ login: function(frm) {
if (frm.doc.consumer_key && frm.doc.consumer_secret){
frappe.dom.freeze();
frappe.call({
@@ -67,7 +68,7 @@
});
}
},
- after_save: function(frm){
+ after_save: function(frm) {
frm.trigger("login");
}
});
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.json b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.json
index 9eacb00..f882e36 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.json
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.json
@@ -2,6 +2,7 @@
"actions": [],
"creation": "2020-01-30 13:36:39.492931",
"doctype": "DocType",
+ "documentation": "https://docs.erpnext.com/docs/user/manual/en/CRM/linkedin-settings",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
@@ -87,7 +88,7 @@
],
"issingle": 1,
"links": [],
- "modified": "2020-04-16 23:22:51.966397",
+ "modified": "2021-02-18 15:19:21.920725",
"modified_by": "Administrator",
"module": "CRM",
"name": "LinkedIn Settings",
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
index d8c6fb4..03c9d9c 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
@@ -3,13 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, requests, json
+
+import frappe
+import requests
from frappe import _
-from frappe.utils import get_site_url, get_url_to_form, get_link_to_form
from frappe.model.document import Document
-from frappe.utils.file_manager import get_file, get_file_path
+from frappe.utils import get_url_to_form
+from frappe.utils.file_manager import get_file_path
from six.moves.urllib.parse import urlencode
+
class LinkedInSettings(Document):
@frappe.whitelist()
def get_authorization_url(self):
@@ -42,11 +45,7 @@
self.db_set("access_token", response["access_token"])
def get_member_profile(self):
- headers = {
- "Authorization": "Bearer {}".format(self.access_token)
- }
- url = "https://api.linkedin.com/v2/me"
- response = requests.get(url=url, headers=headers)
+ 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, {
@@ -55,16 +54,16 @@
"session_status": "Active"
})
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")
- def post(self, text, media=None):
+ def post(self, text, title, media=None):
if not media:
- return self.post_text(text)
+ return self.post_text(text, title)
else:
media_id = self.upload_image(media)
if media_id:
- return self.post_text(text, media_id=media_id)
+ return self.post_text(text, title, media_id=media_id)
else:
frappe.log_error("Failed to upload media.","LinkedIn Upload Error")
@@ -82,9 +81,7 @@
}]
}
}
- headers = {
- "Authorization": "Bearer {}".format(self.access_token)
- }
+ headers = self.get_headers()
response = self.http_post(url=register_url, body=body, headers=headers)
if response.status_code == 200:
@@ -100,24 +97,33 @@
return None
- def post_text(self, text, media_id=None):
+ def post_text(self, text, title, media_id=None):
url = "https://api.linkedin.com/v2/shares"
- headers = {
- "X-Restli-Protocol-Version": "2.0.0",
- "Authorization": "Bearer {}".format(self.access_token),
- "Content-Type": "application/json; charset=UTF-8"
- }
+ headers = self.get_headers()
+ headers["X-Restli-Protocol-Version"] = "2.0.0"
+ headers["Content-Type"] = "application/json; charset=UTF-8"
+
body = {
"distribution": {
"linkedInDistributionTarget": {}
},
"owner":"urn:li:organization:{0}".format(self.company_id),
- "subject": "Test Share Subject",
+ "subject": title,
"text": {
"text": text
}
}
+ reference_url = self.get_reference_url(text)
+ if reference_url:
+ body["content"] = {
+ "contentEntities": [
+ {
+ "entityLocation": reference_url
+ }
+ ]
+ }
+
if media_id:
body["content"]= {
"contentEntities": [{
@@ -141,20 +147,60 @@
raise
except Exception as e:
- content = json.loads(response.content)
-
- if response.status_code == 401:
- self.db_set("session_status", "Expired")
- frappe.db.commit()
- frappe.throw(content["message"], title="LinkedIn Error - Unauthorized")
- elif response.status_code == 403:
- frappe.msgprint(_("You Didn't have permission to access this API"))
- frappe.throw(content["message"], title="LinkedIn Error - Access Denied")
- else:
- frappe.throw(response.reason, title=response.status_code)
+ self.api_error(response)
return response
+ def get_headers(self):
+ 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:
+ return urls[0]
+
+ 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:
+ 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)
+
+ try:
+ response = requests.get(url=url, headers=self.get_headers())
+ if response.status_code !=200:
+ raise
+
+ except Exception:
+ self.api_error(response)
+
+ response = frappe.parse_json(response.content.decode())
+ if len(response.elements):
+ return response.elements[0]
+
+ return None
+
+ def api_error(self, response):
+ content = frappe.parse_json(response.content.decode())
+
+ if response.status_code == 401:
+ self.db_set("session_status", "Expired")
+ frappe.db.commit()
+ frappe.throw(content["message"], title=_("LinkedIn Error - Unauthorized"))
+ elif response.status_code == 403:
+ frappe.msgprint(_("You didn't have permission to access this API"))
+ frappe.throw(content["message"], title=_("LinkedIn Error - Access Denied"))
+ 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:
diff --git a/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py
index 9c3ef3f..1d86f0c 100644
--- a/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/test_linkedin_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLinkedInSettings(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.py b/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.py
index 3baa011..88abd42 100644
--- a/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.py
+++ b/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class LostReasonDetail(Document):
pass
diff --git a/erpnext/crm/doctype/market_segment/market_segment.py b/erpnext/crm/doctype/market_segment/market_segment.py
index 830ea69..92adf56 100644
--- a/erpnext/crm/doctype/market_segment/market_segment.py
+++ b/erpnext/crm/doctype/market_segment/market_segment.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MarketSegment(Document):
pass
diff --git a/erpnext/crm/doctype/market_segment/test_market_segment.js b/erpnext/crm/doctype/market_segment/test_market_segment.js
deleted file mode 100644
index aa4b868..0000000
--- a/erpnext/crm/doctype/market_segment/test_market_segment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Market Segment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Market Segment
- () => frappe.tests.make('Market Segment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/crm/doctype/market_segment/test_market_segment.py b/erpnext/crm/doctype/market_segment/test_market_segment.py
index 2f9ed34..b95cc4c 100644
--- a/erpnext/crm/doctype/market_segment/test_market_segment.py
+++ b/erpnext/crm/doctype/market_segment/test_market_segment.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestMarketSegment(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index e9a7a95..f8376e6 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -10,12 +10,12 @@
frm.custom_make_buttons = {
'Quotation': 'Quotation',
'Supplier Quotation': 'Supplier Quotation'
- },
+ };
frm.set_query("opportunity_from", function() {
return{
"filters": {
- "name": ["in", ["Customer", "Lead"]],
+ "name": ["in", ["Customer", "Lead", "Prospect"]],
}
}
});
@@ -57,7 +57,7 @@
if (frm.doc.status == "Lost"){
frm.trigger('set_as_lost_dialog');
}
-
+
},
customer_address: function(frm, cdt, cdn) {
@@ -95,9 +95,17 @@
}, __('Create'));
}
- frm.add_custom_button(__('Quotation'),
- cur_frm.cscript.create_quotation, __('Create'));
+ if (frm.doc.opportunity_from != "Customer") {
+ frm.add_custom_button(__('Customer'),
+ function() {
+ frm.trigger("make_customer")
+ }, __('Create'));
+ }
+ frm.add_custom_button(__('Quotation'),
+ function() {
+ frm.trigger("create_quotation")
+ }, __('Create'));
}
if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) {
@@ -124,10 +132,43 @@
}
},
+ currency: function(frm) {
+ let company_currency = erpnext.get_currency(frm.doc.company);
+ if (company_currency != frm.doc.company) {
+ frappe.call({
+ method: "erpnext.setup.utils.get_exchange_rate",
+ args: {
+ from_currency: frm.doc.currency,
+ to_currency: company_currency
+ },
+ callback: function(r) {
+ if (r.message) {
+ frm.set_value('conversion_rate', flt(r.message));
+ frm.set_df_property('conversion_rate', 'description', '1 ' + frm.doc.currency
+ + ' = [?] ' + company_currency);
+ }
+ }
+ });
+ } else {
+ frm.set_value('conversion_rate', 1.0);
+ frm.set_df_property('conversion_rate', 'hidden', 1);
+ frm.set_df_property('conversion_rate', 'description', '');
+ }
+
+ frm.trigger('opportunity_amount');
+ frm.trigger('set_dynamic_field_label');
+ },
+
+ opportunity_amount: function(frm) {
+ frm.set_value('base_opportunity_amount', flt(frm.doc.opportunity_amount) * flt(frm.doc.conversion_rate));
+ },
+
set_dynamic_field_label: function(frm){
if (frm.doc.opportunity_from) {
frm.set_df_property("party_name", "label", frm.doc.opportunity_from);
}
+ frm.trigger('change_grid_labels');
+ frm.trigger('change_form_labels');
},
make_supplier_quotation: function(frm) {
@@ -144,6 +185,62 @@
})
},
+ change_form_labels: function(frm) {
+ let company_currency = erpnext.get_currency(frm.doc.company);
+ frm.set_currency_labels(["base_opportunity_amount", "base_total", "base_grand_total"], company_currency);
+ frm.set_currency_labels(["opportunity_amount", "total", "grand_total"], frm.doc.currency);
+
+ // toggle fields
+ frm.toggle_display(["conversion_rate", "base_opportunity_amount", "base_total", "base_grand_total"],
+ frm.doc.currency != company_currency);
+ },
+
+ change_grid_labels: function(frm) {
+ let company_currency = erpnext.get_currency(frm.doc.company);
+ frm.set_currency_labels(["base_rate", "base_amount"], company_currency, "items");
+ frm.set_currency_labels(["rate", "amount"], frm.doc.currency, "items");
+
+ let item_grid = frm.fields_dict.items.grid;
+ $.each(["base_rate", "base_amount"], function(i, fname) {
+ if(frappe.meta.get_docfield(item_grid.doctype, fname))
+ item_grid.set_column_disp(fname, frm.doc.currency != company_currency);
+ });
+ frm.refresh_fields();
+ },
+
+ calculate_total: function(frm) {
+ let total = 0, base_total = 0, grand_total = 0, base_grand_total = 0;
+ frm.doc.items.forEach(item => {
+ total += item.amount;
+ base_total += item.base_amount;
+ })
+
+ base_grand_total = base_total + frm.doc.base_opportunity_amount;
+ grand_total = total + frm.doc.opportunity_amount;
+
+ frm.set_value({
+ 'total': flt(total),
+ 'base_total': flt(base_total),
+ 'grand_total': flt(grand_total),
+ 'base_grand_total': flt(base_grand_total)
+ });
+ }
+
+});
+frappe.ui.form.on("Opportunity Item", {
+ calculate: function(frm, cdt, cdn) {
+ let row = frappe.get_doc(cdt, cdn);
+ frappe.model.set_value(cdt, cdn, "amount", flt(row.qty) * flt(row.rate));
+ frappe.model.set_value(cdt, cdn, "base_rate", flt(frm.doc.conversion_rate) * flt(row.rate));
+ frappe.model.set_value(cdt, cdn, "base_amount", flt(frm.doc.conversion_rate) * flt(row.amount));
+ frm.trigger("calculate_total");
+ },
+ qty: function(frm, cdt, cdn) {
+ frm.trigger("calculate", cdt, cdn);
+ },
+ rate: function(frm, cdt, cdn) {
+ frm.trigger("calculate", cdt, cdn);
+ }
})
// TODO commonify this code
@@ -161,6 +258,7 @@
}
this.setup_queries();
+ this.frm.trigger('currency');
}
setup_queries() {
@@ -195,6 +293,13 @@
frm: cur_frm
})
}
+
+ make_customer() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.crm.doctype.opportunity.opportunity.make_customer",
+ frm: cur_frm
+ })
+ }
};
extend_cscript(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index 4ba4140..dc886b5 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -33,12 +33,20 @@
"to_discuss",
"section_break_14",
"currency",
- "opportunity_amount",
+ "conversion_rate",
+ "base_opportunity_amount",
"with_items",
"column_break_17",
"probability",
+ "opportunity_amount",
"items_section",
"items",
+ "section_break_32",
+ "base_total",
+ "base_grand_total",
+ "column_break_33",
+ "total",
+ "grand_total",
"contact_info",
"customer_address",
"address_display",
@@ -425,12 +433,65 @@
"fieldtype": "Link",
"label": "Print Language",
"options": "Language"
+ },
+ {
+ "fieldname": "base_opportunity_amount",
+ "fieldtype": "Currency",
+ "label": "Opportunity Amount (Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "depends_on": "with_items",
+ "fieldname": "section_break_32",
+ "fieldtype": "Section Break",
+ "hide_border": 1
+ },
+ {
+ "fieldname": "base_total",
+ "fieldtype": "Currency",
+ "label": "Total (Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "total",
+ "fieldtype": "Currency",
+ "label": "Total",
+ "options": "currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "conversion_rate",
+ "fieldtype": "Float",
+ "label": "Exchange Rate"
+ },
+ {
+ "fieldname": "column_break_33",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "base_grand_total",
+ "fieldtype": "Currency",
+ "label": "Grand Total (Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "grand_total",
+ "fieldtype": "Currency",
+ "label": "Grand Total",
+ "options": "currency",
+ "read_only": 1
}
],
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
- "modified": "2021-06-04 10:11:22.831139",
+ "modified": "2021-09-06 10:02:18.609136",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 23ad98a..be843a3 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -2,14 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.utils import cstr, cint, get_fullname
-from frappe import msgprint, _
+
+import json
+
+import frappe
+from frappe import _
+from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, cstr, flt, get_fullname
+
from erpnext.setup.utils import get_exchange_rate
from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.accounts.party import get_party_account_currency
-from frappe.email.inbox import link_communication_to_document
+
class Opportunity(TransactionBase):
def after_insert(self):
@@ -36,6 +40,23 @@
if not self.with_items:
self.items = []
+ else:
+ self.calculate_totals()
+
+ def calculate_totals(self):
+ total = base_total = 0
+ 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)
+ total += item.amount
+ base_total += item.base_amount
+
+ self.total = flt(total)
+ self.base_total = flt(base_total)
+ self.grand_total = flt(self.total) + flt(self.opportunity_amount)
+ self.base_grand_total = flt(self.base_total) + flt(self.base_opportunity_amount)
+
def make_new_lead_if_required(self):
"""Set lead against new opportunity"""
if (not self.get("party_name")) and self.contact_email:
@@ -219,13 +240,6 @@
company_currency = frappe.get_cached_value('Company', quotation.company, "default_currency")
- if quotation.quotation_to == 'Customer' and quotation.party_name:
- party_account_currency = get_party_account_currency("Customer", quotation.party_name, quotation.company)
- else:
- party_account_currency = company_currency
-
- quotation.currency = party_account_currency or company_currency
-
if company_currency == quotation.currency:
exchange_rate = 1
else:
@@ -249,7 +263,7 @@
"doctype": "Quotation",
"field_map": {
"opportunity_from": "quotation_to",
- "name": "enq_no",
+ "name": "enq_no"
}
},
"Opportunity Item": {
@@ -288,6 +302,24 @@
return doclist
@frappe.whitelist()
+def make_customer(source_name, target_doc=None):
+ def set_missing_values(source, target):
+ 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"
+ }
+ }
+ }, 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": {
@@ -372,4 +404,4 @@
"start": start,
"end": end
}, as_dict=True, update={"allDay": 0})
- return data
\ No newline at end of file
+ return data
diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
index 68f0104..693a86c 100644
--- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
+++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Quotation', 'Supplier Quotation']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py
index 04cd8a2..65d6cb3 100644
--- a/erpnext/crm/doctype/opportunity/test_opportunity.py
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.py
@@ -2,11 +2,13 @@
# See license.txt
from __future__ import unicode_literals
+import unittest
+
import frappe
-from frappe.utils import today, random_string
+from frappe.utils import random_string, today
+
from erpnext.crm.doctype.lead.lead import make_customer
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
-import unittest
test_records = frappe.get_test_records('Opportunity')
@@ -61,6 +63,10 @@
self.assertEqual(opp_doc.opportunity_from, "Customer")
self.assertEqual(opp_doc.party_name, customer.name)
+ def test_opportunity_item(self):
+ opportunity_doc = make_opportunity(with_items=1, rate=1100, qty=2)
+ self.assertEqual(opportunity_doc.total, 2200)
+
def make_opportunity(**args):
args = frappe._dict(args)
@@ -69,6 +75,7 @@
"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()
})
@@ -83,8 +90,9 @@
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
\ No newline at end of file
+ return opp_doc
diff --git a/erpnext/crm/doctype/opportunity_item/opportunity_item.json b/erpnext/crm/doctype/opportunity_item/opportunity_item.json
index 65e8433..1b4973c 100644
--- a/erpnext/crm/doctype/opportunity_item/opportunity_item.json
+++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.json
@@ -1,469 +1,177 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-02-22 01:27:51",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2013-02-22 01:27:51",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "item_code",
+ "item_name",
+ "col_break1",
+ "uom",
+ "qty",
+ "section_break_6",
+ "brand",
+ "item_group",
+ "description",
+ "column_break_8",
+ "image",
+ "image_view",
+ "quantity_and_rate_section",
+ "base_rate",
+ "base_amount",
+ "column_break_16",
+ "rate",
+ "amount"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_code",
- "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": "Item Code",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "item_code",
- "oldfieldtype": "Link",
- "options": "Item",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Code",
+ "oldfieldname": "item_code",
+ "oldfieldtype": "Link",
+ "options": "Item"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "col_break1",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "col_break1",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "qty",
- "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": "Qty",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "1",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Qty",
+ "oldfieldname": "qty",
+ "oldfieldtype": "Currency"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "item_group",
- "fieldtype": "Link",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Item Group",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "item_group",
- "oldfieldtype": "Link",
- "options": "Item Group",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_group",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Item Group",
+ "oldfieldname": "item_group",
+ "oldfieldtype": "Link",
+ "options": "Item Group",
+ "print_hide": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "brand",
- "fieldtype": "Link",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Brand",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "brand",
- "oldfieldtype": "Link",
- "options": "Brand",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "brand",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Brand",
+ "oldfieldname": "brand",
+ "oldfieldtype": "Link",
+ "options": "Brand",
+ "print_hide": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_6",
- "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
- },
+ "collapsible": 1,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "label": "Description"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "uom",
- "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": "UOM",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "uom",
- "oldfieldtype": "Link",
- "options": "UOM",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "uom",
+ "fieldtype": "Link",
+ "label": "UOM",
+ "oldfieldname": "uom",
+ "oldfieldtype": "Link",
+ "options": "UOM"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_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": "Item Name",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "item_name",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Item Name",
+ "oldfieldname": "item_name",
+ "oldfieldtype": "Data"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "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": "Description",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "description",
- "oldfieldtype": "Text",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "300px",
- "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": "description",
+ "fieldtype": "Text Editor",
+ "label": "Description",
+ "oldfieldname": "description",
+ "oldfieldtype": "Text",
+ "print_width": "300px",
"width": "300px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_8",
- "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_8",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "image",
- "fieldtype": "Attach",
- "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": 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": "image",
+ "fieldtype": "Attach",
+ "hidden": 1,
+ "label": "Image"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "image_view",
- "fieldtype": "Image",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Image View",
- "length": 0,
- "no_copy": 0,
- "options": "image",
- "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,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "image_view",
+ "fieldtype": "Image",
+ "label": "Image View",
+ "options": "image",
+ "print_hide": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "basic_rate",
- "fieldtype": "Currency",
- "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": "Basic Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "basic_rate",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Rate",
+ "options": "currency",
+ "reqd": 1
+ },
+ {
+ "fieldname": "quantity_and_rate_section",
+ "fieldtype": "Section Break",
+ "label": "Quantity and Rate"
+ },
+ {
+ "fieldname": "base_amount",
+ "fieldtype": "Currency",
+ "label": "Amount (Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount",
+ "options": "currency",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "base_rate",
+ "fieldtype": "Currency",
+ "label": "Rate (Company Currency)",
+ "oldfieldname": "basic_rate",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1,
+ "reqd": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-12-28 15:43:09.382012",
- "modified_by": "Administrator",
- "module": "CRM",
- "name": "Opportunity Item",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-07-30 16:39:09.775720",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Opportunity Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/opportunity_item/opportunity_item.py b/erpnext/crm/doctype/opportunity_item/opportunity_item.py
index 7a5ed63..225cfe9 100644
--- a/erpnext/crm/doctype/opportunity_item/opportunity_item.py
+++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class OpportunityItem(Document):
pass
diff --git a/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.py b/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.py
index 48b63b0..b25f02a 100644
--- a/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.py
+++ b/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class OpportunityLostReason(Document):
pass
diff --git a/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py b/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py
index 8723f1d..4bb5c54 100644
--- a/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py
+++ b/erpnext/crm/doctype/opportunity_lost_reason_detail/opportunity_lost_reason_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class OpportunityLostReasonDetail(Document):
pass
diff --git a/erpnext/crm/doctype/opportunity_type/opportunity_type.py b/erpnext/crm/doctype/opportunity_type/opportunity_type.py
index 48abac3..5b64e980 100644
--- a/erpnext/crm/doctype/opportunity_type/opportunity_type.py
+++ b/erpnext/crm/doctype/opportunity_type/opportunity_type.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class OpportunityType(Document):
pass
diff --git a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.js b/erpnext/crm/doctype/opportunity_type/test_opportunity_type.js
deleted file mode 100644
index 3a1ede9..0000000
--- a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Opportunity Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Opportunity Type
- () => frappe.tests.make('Opportunity Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.py b/erpnext/crm/doctype/opportunity_type/test_opportunity_type.py
index 6410bbc..f86a70e 100644
--- a/erpnext/crm/doctype/opportunity_type/test_opportunity_type.py
+++ b/erpnext/crm/doctype/opportunity_type/test_opportunity_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestOpportunityType(unittest.TestCase):
pass
diff --git a/erpnext/healthcare/report/__init__.py b/erpnext/crm/doctype/prospect/__init__.py
similarity index 100%
copy from erpnext/healthcare/report/__init__.py
copy to erpnext/crm/doctype/prospect/__init__.py
diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js
new file mode 100644
index 0000000..67018e1
--- /dev/null
+++ b/erpnext/crm/doctype/prospect/prospect.js
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Prospect', {
+ refresh (frm) {
+ if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) {
+ frm.add_custom_button(__("Customer"), function() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.crm.doctype.prospect.prospect.make_customer",
+ frm: frm
+ });
+ }, __("Create"));
+ }
+ if (!frm.is_new() && frappe.boot.user.can_create.includes("Opportunity")) {
+ frm.add_custom_button(__("Opportunity"), function() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.crm.doctype.prospect.prospect.make_opportunity",
+ frm: frm
+ });
+ }, __("Create"));
+ }
+
+ if (!frm.is_new()) {
+ frappe.contacts.render_address_and_contact(frm);
+ } else {
+ frappe.contacts.clear_address_and_contact(frm);
+ }
+ }
+});
diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json
new file mode 100644
index 0000000..3d6fba5
--- /dev/null
+++ b/erpnext/crm/doctype/prospect/prospect.json
@@ -0,0 +1,203 @@
+{
+ "actions": [],
+ "autoname": "field:company_name",
+ "creation": "2021-08-19 00:21:06.995448",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company_name",
+ "industry",
+ "market_segment",
+ "customer_group",
+ "territory",
+ "column_break_6",
+ "no_of_employees",
+ "currency",
+ "annual_revenue",
+ "more_details_section",
+ "fax",
+ "website",
+ "column_break_13",
+ "prospect_owner",
+ "leads_section",
+ "prospect_lead",
+ "address_and_contact_section",
+ "address_html",
+ "column_break_17",
+ "contact_html",
+ "notes_section",
+ "notes"
+ ],
+ "fields": [
+ {
+ "fieldname": "company_name",
+ "fieldtype": "Data",
+ "label": "Company Name",
+ "unique": 1
+ },
+ {
+ "fieldname": "industry",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Industry",
+ "options": "Industry Type"
+ },
+ {
+ "fieldname": "market_segment",
+ "fieldtype": "Link",
+ "label": "Market Segment",
+ "options": "Market Segment"
+ },
+ {
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "label": "Customer Group",
+ "options": "Customer Group"
+ },
+ {
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Territory",
+ "options": "Territory"
+ },
+ {
+ "fieldname": "column_break_6",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "no_of_employees",
+ "fieldtype": "Int",
+ "label": "No. of Employees"
+ },
+ {
+ "fieldname": "currency",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Currency",
+ "options": "Currency"
+ },
+ {
+ "fieldname": "annual_revenue",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Annual Revenue",
+ "options": "currency"
+ },
+ {
+ "fieldname": "fax",
+ "fieldtype": "Data",
+ "label": "Fax",
+ "options": "Phone"
+ },
+ {
+ "fieldname": "website",
+ "fieldtype": "Data",
+ "label": "Website",
+ "options": "URL"
+ },
+ {
+ "fieldname": "prospect_owner",
+ "fieldtype": "Link",
+ "label": "Prospect Owner",
+ "options": "User"
+ },
+ {
+ "fieldname": "leads_section",
+ "fieldtype": "Section Break",
+ "label": "Leads"
+ },
+ {
+ "fieldname": "prospect_lead",
+ "fieldtype": "Table",
+ "options": "Prospect Lead"
+ },
+ {
+ "fieldname": "address_html",
+ "fieldtype": "HTML",
+ "label": "Address HTML"
+ },
+ {
+ "fieldname": "column_break_17",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "contact_html",
+ "fieldtype": "HTML",
+ "label": "Contact HTML"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "notes_section",
+ "fieldtype": "Section Break",
+ "label": "Notes"
+ },
+ {
+ "fieldname": "notes",
+ "fieldtype": "Text Editor"
+ },
+ {
+ "fieldname": "more_details_section",
+ "fieldtype": "Section Break",
+ "label": "More Details"
+ },
+ {
+ "fieldname": "column_break_13",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: !doc.__islocal",
+ "fieldname": "address_and_contact_section",
+ "fieldtype": "Section Break",
+ "label": "Address and Contact"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-08-27 16:24:42.961967",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Prospect",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 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": "Sales Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales User",
+ "share": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "company_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py
new file mode 100644
index 0000000..367aa3d
--- /dev/null
+++ b/erpnext/crm/doctype/prospect/prospect.py
@@ -0,0 +1,100 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.contacts.address_and_contact import load_address_and_contact
+from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
+
+
+class Prospect(Document):
+ def onload(self):
+ load_address_and_contact(self)
+
+ def validate(self):
+ self.update_lead_details()
+
+ def on_update(self):
+ self.link_with_lead_contact_and_address()
+
+ def on_trash(self):
+ self.unlink_dynamic_links()
+
+ 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)
+ row.lead_name = lead.lead_name
+ row.status = lead.status
+ row.email = lead.email_id
+ row.mobile_no = lead.mobile_no
+
+ 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'])
+ for link in links:
+ linked_doc = frappe.get_doc(link['parenttype'], link['parent'])
+ exists = False
+
+ 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.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'])
+
+ for link in links:
+ linked_doc = frappe.get_doc(link['parenttype'], link['parent'])
+
+ if len(linked_doc.get('links')) == 1:
+ linked_doc.delete(ignore_permissions=True)
+ else:
+ to_remove = None
+ 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):
+ target.customer_type = "Company"
+ 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"
+ }
+ }}, 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):
+ target.opportunity_from = "Prospect"
+ 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",
+ }
+ }}, 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
new file mode 100644
index 0000000..fa44e20
--- /dev/null
+++ b/erpnext/crm/doctype/prospect/test_prospect.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import unittest
+
+import frappe
+from frappe.utils import random_string
+
+from erpnext.crm.doctype.lead.lead import add_lead_to_prospect
+from erpnext.crm.doctype.lead.test_lead import make_lead
+
+
+class TestProspect(unittest.TestCase):
+ 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.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'):
+ 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)
+
+
+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()
+
+ 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()
+
+ return address_doc
diff --git a/erpnext/healthcare/__init__.py b/erpnext/crm/doctype/prospect_lead/__init__.py
similarity index 100%
copy from erpnext/healthcare/__init__.py
copy to erpnext/crm/doctype/prospect_lead/__init__.py
diff --git a/erpnext/crm/doctype/prospect_lead/prospect_lead.json b/erpnext/crm/doctype/prospect_lead/prospect_lead.json
new file mode 100644
index 0000000..3c160d9
--- /dev/null
+++ b/erpnext/crm/doctype/prospect_lead/prospect_lead.json
@@ -0,0 +1,67 @@
+{
+ "actions": [],
+ "creation": "2021-08-19 00:14:14.857421",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "lead",
+ "lead_name",
+ "status",
+ "email",
+ "mobile_no"
+ ],
+ "fields": [
+ {
+ "fieldname": "lead",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Lead",
+ "options": "Lead",
+ "reqd": 1
+ },
+ {
+ "fieldname": "lead_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Lead Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Lead\nOpen\nReplied\nOpportunity\nQuotation\nLost Quotation\nInterested\nConverted\nDo Not Contact",
+ "read_only": 1
+ },
+ {
+ "fieldname": "email",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Email",
+ "options": "Email",
+ "read_only": 1
+ },
+ {
+ "fieldname": "mobile_no",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Mobile No",
+ "options": "Phone",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-25 12:58:24.638054",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Prospect Lead",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/prospect_lead/prospect_lead.py b/erpnext/crm/doctype/prospect_lead/prospect_lead.py
new file mode 100644
index 0000000..40edbe0
--- /dev/null
+++ b/erpnext/crm/doctype/prospect_lead/prospect_lead.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class ProspectLead(Document):
+ pass
diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.py b/erpnext/crm/doctype/sales_stage/sales_stage.py
index a80f4cc..e9e176f 100644
--- a/erpnext/crm/doctype/sales_stage/sales_stage.py
+++ b/erpnext/crm/doctype/sales_stage/sales_stage.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SalesStage(Document):
pass
diff --git a/erpnext/crm/doctype/sales_stage/test_sales_stage.js b/erpnext/crm/doctype/sales_stage/test_sales_stage.js
deleted file mode 100644
index 807af1f..0000000
--- a/erpnext/crm/doctype/sales_stage/test_sales_stage.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Stage", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Sales Stage
- () => frappe.tests.make('Sales Stage', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/crm/doctype/sales_stage/test_sales_stage.py b/erpnext/crm/doctype/sales_stage/test_sales_stage.py
index 80b6513..83d1f26 100644
--- a/erpnext/crm/doctype/sales_stage/test_sales_stage.py
+++ b/erpnext/crm/doctype/sales_stage/test_sales_stage.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestSalesStage(unittest.TestCase):
pass
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 0ce8b44..6874caa 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.js
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.js
@@ -1,67 +1,139 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Social Media Post', {
- validate: function(frm){
- if (frm.doc.twitter === 0 && frm.doc.linkedin === 0){
- frappe.throw(__("Select atleast one Social Media from Share on."))
- }
- if (frm.doc.scheduled_time) {
- let scheduled_time = new Date(frm.doc.scheduled_time);
- let date_time = new Date();
- if (scheduled_time.getTime() < date_time.getTime()){
- frappe.throw(__("Invalid Scheduled Time"));
- }
- }
- if (frm.doc.text?.length > 280){
- frappe.throw(__("Length Must be less than 280."))
- }
- },
- refresh: function(frm){
- if (frm.doc.docstatus === 1){
- if (frm.doc.post_status != "Posted"){
- add_post_btn(frm);
- }
- else if (frm.doc.post_status == "Posted"){
- frm.set_df_property('sheduled_time', 'read_only', 1);
- }
+ validate: function(frm) {
+ if (frm.doc.twitter === 0 && frm.doc.linkedin === 0) {
+ frappe.throw(__("Select atleast one Social Media Platform to Share on."));
+ }
+ if (frm.doc.scheduled_time) {
+ let scheduled_time = new Date(frm.doc.scheduled_time);
+ let date_time = new Date();
+ if (scheduled_time.getTime() < date_time.getTime()) {
+ frappe.throw(__("Scheduled Time must be a future time."));
+ }
+ }
+ frm.trigger('validate_tweet_length');
+ },
- let html='';
- if (frm.doc.twitter){
- let color = frm.doc.twitter_post_id ? "green" : "red";
- let status = frm.doc.twitter_post_id ? "Posted" : "Not Posted";
- html += `<div class="col-xs-6">
- <span class="indicator whitespace-nowrap ${color}"><span>Twitter : ${status} </span></span>
- </div>` ;
- }
- if (frm.doc.linkedin){
- let color = frm.doc.linkedin_post_id ? "green" : "red";
- let status = frm.doc.linkedin_post_id ? "Posted" : "Not Posted";
- html += `<div class="col-xs-6">
- <span class="indicator whitespace-nowrap ${color}"><span>LinkedIn : ${status} </span></span>
- </div>` ;
- }
- html = `<div class="row">${html}</div>`;
- frm.dashboard.set_headline_alert(html);
- }
- }
+ text: function(frm) {
+ if (frm.doc.text) {
+ frm.set_df_property('text', 'description', `${frm.doc.text.length}/280`);
+ frm.refresh_field('text');
+ frm.trigger('validate_tweet_length');
+ }
+ },
+
+ validate_tweet_length: function(frm) {
+ if (frm.doc.text && frm.doc.text.length > 280) {
+ frappe.throw(__("Tweet length Must be less than 280."));
+ }
+ },
+
+ onload: function(frm) {
+ frm.trigger('make_dashboard');
+ },
+
+ make_dashboard: function(frm) {
+ if (frm.doc.post_status == "Posted") {
+ frappe.call({
+ doc: frm.doc,
+ method: 'get_post',
+ freeze: true,
+ callback: (r) => {
+ if (!r.message) {
+ return;
+ }
+
+ let datasets = [], colors = [];
+ if (r.message && r.message.twitter) {
+ colors.push('#1DA1F2');
+ datasets.push({
+ name: 'Twitter',
+ values: [r.message.twitter.favorite_count, r.message.twitter.retweet_count]
+ });
+ }
+ if (r.message && r.message.linkedin) {
+ colors.push('#0077b5');
+ datasets.push({
+ name: 'LinkedIn',
+ values: [r.message.linkedin.totalShareStatistics.likeCount, r.message.linkedin.totalShareStatistics.shareCount]
+ });
+ }
+
+ if (datasets.length) {
+ frm.dashboard.render_graph({
+ data: {
+ labels: ['Likes', 'Retweets/Shares'],
+ datasets: datasets
+ },
+
+ title: __("Post Metrics"),
+ type: 'bar',
+ height: 300,
+ colors: colors
+ });
+ }
+ }
+ });
+ }
+ },
+
+ refresh: function(frm) {
+ frm.trigger('text');
+
+ if (frm.doc.docstatus === 1) {
+ if (!['Posted', 'Deleted'].includes(frm.doc.post_status)) {
+ frm.trigger('add_post_btn');
+ }
+ if (frm.doc.post_status !='Deleted') {
+ frm.add_custom_button(('Delete Post'), function() {
+ frappe.confirm(__('Are you sure want to delete the Post from Social Media platforms?'),
+ function() {
+ frappe.call({
+ doc: frm.doc,
+ method: 'delete_post',
+ freeze: true,
+ callback: () => {
+ frm.reload_doc();
+ }
+ });
+ }
+ );
+ });
+ }
+
+ if (frm.doc.post_status !='Deleted') {
+ let html='';
+ if (frm.doc.twitter) {
+ let color = frm.doc.twitter_post_id ? "green" : "red";
+ let status = frm.doc.twitter_post_id ? "Posted" : "Not Posted";
+ html += `<div class="col-xs-6">
+ <span class="indicator whitespace-nowrap ${color}"><span>Twitter : ${status} </span></span>
+ </div>` ;
+ }
+ if (frm.doc.linkedin) {
+ let color = frm.doc.linkedin_post_id ? "green" : "red";
+ let status = frm.doc.linkedin_post_id ? "Posted" : "Not Posted";
+ html += `<div class="col-xs-6">
+ <span class="indicator whitespace-nowrap ${color}"><span>LinkedIn : ${status} </span></span>
+ </div>` ;
+ }
+ html = `<div class="row">${html}</div>`;
+ frm.dashboard.set_headline_alert(html);
+ }
+ }
+ },
+
+ add_post_btn: function(frm) {
+ frm.add_custom_button(__('Post Now'), function() {
+ frappe.call({
+ doc: frm.doc,
+ method: 'post',
+ freeze: true,
+ callback: function() {
+ frm.reload_doc();
+ }
+ });
+ });
+ }
});
-var add_post_btn = function(frm){
- frm.add_custom_button(('Post Now'), function(){
- post(frm);
- });
-}
-var post = function(frm){
- frappe.dom.freeze();
- frappe.call({
- method: "erpnext.crm.doctype.social_media_post.social_media_post.publish",
- args: {
- doctype: frm.doc.doctype,
- name: frm.doc.name
- },
- callback: function(r) {
- frm.reload_doc();
- frappe.dom.unfreeze();
- }
- })
-
-}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json
index 0a00dca..98e78f9 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.json
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.json
@@ -3,9 +3,11 @@
"autoname": "format: CRM-SMP-{YYYY}-{MM}-{DD}-{###}",
"creation": "2020-01-30 11:53:13.872864",
"doctype": "DocType",
+ "documentation": "https://docs.erpnext.com/docs/user/manual/en/CRM/social-media-post",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
+ "title",
"campaign_name",
"scheduled_time",
"post_status",
@@ -30,32 +32,24 @@
"fieldname": "text",
"fieldtype": "Small Text",
"label": "Tweet",
- "mandatory_depends_on": "eval:doc.twitter ==1",
- "show_days": 1,
- "show_seconds": 1
+ "mandatory_depends_on": "eval:doc.twitter ==1"
},
{
"fieldname": "image",
"fieldtype": "Attach Image",
- "label": "Image",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Image"
},
{
- "default": "0",
+ "default": "1",
"fieldname": "twitter",
"fieldtype": "Check",
- "label": "Twitter",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Twitter"
},
{
- "default": "0",
+ "default": "1",
"fieldname": "linkedin",
"fieldtype": "Check",
- "label": "LinkedIn",
- "show_days": 1,
- "show_seconds": 1
+ "label": "LinkedIn"
},
{
"fieldname": "amended_from",
@@ -64,27 +58,22 @@
"no_copy": 1,
"options": "Social Media Post",
"print_hide": 1,
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "read_only": 1
},
{
"depends_on": "eval:doc.twitter ==1",
"fieldname": "content",
"fieldtype": "Section Break",
- "label": "Twitter",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Twitter"
},
{
"allow_on_submit": 1,
"fieldname": "post_status",
"fieldtype": "Select",
"label": "Post Status",
- "options": "\nScheduled\nPosted\nError",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "no_copy": 1,
+ "options": "\nScheduled\nPosted\nCancelled\nDeleted\nError",
+ "read_only": 1
},
{
"allow_on_submit": 1,
@@ -92,9 +81,8 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Twitter Post Id",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "no_copy": 1,
+ "read_only": 1
},
{
"allow_on_submit": 1,
@@ -102,82 +90,69 @@
"fieldtype": "Data",
"hidden": 1,
"label": "LinkedIn Post Id",
- "read_only": 1,
- "show_days": 1,
- "show_seconds": 1
+ "no_copy": 1,
+ "read_only": 1
},
{
"fieldname": "campaign_name",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Campaign",
- "options": "Campaign",
- "show_days": 1,
- "show_seconds": 1
+ "options": "Campaign"
},
{
"fieldname": "column_break_6",
"fieldtype": "Column Break",
- "label": "Share On",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Share On"
},
{
"fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"fieldname": "tweet_preview",
- "fieldtype": "HTML",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "HTML"
},
{
"collapsible": 1,
"depends_on": "eval:doc.linkedin==1",
"fieldname": "linkedin_section",
"fieldtype": "Section Break",
- "label": "LinkedIn",
- "show_days": 1,
- "show_seconds": 1
+ "label": "LinkedIn"
},
{
"collapsible": 1,
"fieldname": "attachments_section",
"fieldtype": "Section Break",
- "label": "Attachments",
- "show_days": 1,
- "show_seconds": 1
+ "label": "Attachments"
},
{
"fieldname": "linkedin_post",
"fieldtype": "Text",
"label": "Post",
- "mandatory_depends_on": "eval:doc.linkedin ==1",
- "show_days": 1,
- "show_seconds": 1
+ "mandatory_depends_on": "eval:doc.linkedin ==1"
},
{
"fieldname": "column_break_15",
- "fieldtype": "Column Break",
- "show_days": 1,
- "show_seconds": 1
+ "fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "scheduled_time",
"fieldtype": "Datetime",
"label": "Scheduled Time",
- "read_only_depends_on": "eval:doc.post_status == \"Posted\"",
- "show_days": 1,
- "show_seconds": 1
+ "read_only_depends_on": "eval:doc.post_status == \"Posted\""
+ },
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "label": "Title",
+ "reqd": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-06-14 10:31:33.961381",
+ "modified": "2021-04-14 14:24:59.821223",
"modified_by": "Administrator",
"module": "CRM",
"name": "Social Media Post",
@@ -228,5 +203,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "title_field": "title",
"track_changes": 1
}
\ No newline at end of file
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 ed1b583..14d4521 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.py
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.py
@@ -3,24 +3,61 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe import _
+
import datetime
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+
class SocialMediaPost(Document):
def validate(self):
+ if (not self.twitter and not self.linkedin):
+ frappe.throw(_("Select atleast one Social Media Platform to Share on."))
+
if self.scheduled_time:
current_time = frappe.utils.now_datetime()
scheduled_time = frappe.utils.get_datetime(self.scheduled_time)
if scheduled_time < current_time:
- frappe.throw(_("Invalid Scheduled Time"))
+ frappe.throw(_("Scheduled Time must be a future time."))
+
+ if self.text and len(self.text) > 280:
+ frappe.throw(_("Tweet length must be less than 280."))
def submit(self):
if self.scheduled_time:
self.post_status = "Scheduled"
super(SocialMediaPost, self).submit()
+ def on_cancel(self):
+ self.db_set('post_status', 'Cancelled')
+
+ @frappe.whitelist()
+ def delete_post(self):
+ if self.twitter and self.twitter_post_id:
+ twitter = frappe.get_doc("Twitter Settings")
+ twitter.delete_tweet(self.twitter_post_id)
+
+ if self.linkedin and self.linkedin_post_id:
+ linkedin = frappe.get_doc("LinkedIn Settings")
+ linkedin.delete_post(self.linkedin_post_id)
+
+ 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)
+ if self.twitter and self.twitter_post_id:
+ twitter = frappe.get_doc("Twitter Settings")
+ response['twitter'] = twitter.get_tweet(self.twitter_post_id)
+
+ return response
+
+ @frappe.whitelist()
def post(self):
try:
if self.twitter and not self.twitter_post_id:
@@ -29,28 +66,22 @@
self.db_set("twitter_post_id", twitter_post.id)
if self.linkedin and not self.linkedin_post_id:
linkedin = frappe.get_doc("LinkedIn Settings")
- linkedin_post = linkedin.post(self.linkedin_post, self.image)
- self.db_set("linkedin_post_id", linkedin_post.headers['X-RestLi-Id'].split(":")[-1])
+ 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("post_status", "Posted")
- except:
+ except Exception:
self.db_set("post_status", "Error")
title = _("Error while POSTING {0}").format(self.name)
- traceback = frappe.get_traceback()
- frappe.log_error(message=traceback , title=title)
+ frappe.log_error(message=frappe.get_traceback(), title=title)
def process_scheduled_social_media_posts():
- posts = frappe.get_list("Social Media Post", filters={"post_status": "Scheduled", "docstatus":1}, fields= ["name", "scheduled_time","post_status"])
+ 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:
- publish('Social Media Post', post.name)
-
-@frappe.whitelist()
-def publish(doctype, name):
- sm_post = frappe.get_doc(doctype, name)
- sm_post.post()
- frappe.db.commit()
+ sm_post = frappe.get_doc('Social Media Post', post.name)
+ sm_post.post()
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post_list.js b/erpnext/crm/doctype/social_media_post/social_media_post_list.js
index c60b91a..a8c8272 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post_list.js
+++ b/erpnext/crm/doctype/social_media_post/social_media_post_list.js
@@ -1,10 +1,11 @@
frappe.listview_settings['Social Media Post'] = {
- add_fields: ["status","post_status"],
- get_indicator: function(doc) {
- return [__(doc.post_status), {
- "Scheduled": "orange",
- "Posted": "green",
- "Error": "red"
- }[doc.post_status]];
- }
+ add_fields: ["status", "post_status"],
+ get_indicator: function(doc) {
+ return [__(doc.post_status), {
+ "Scheduled": "orange",
+ "Posted": "green",
+ "Error": "red",
+ "Deleted": "red"
+ }[doc.post_status]];
+ }
}
diff --git a/erpnext/crm/doctype/social_media_post/test_social_media_post.py b/erpnext/crm/doctype/social_media_post/test_social_media_post.py
index ec81ee5..c2d0418 100644
--- a/erpnext/crm/doctype/social_media_post/test_social_media_post.py
+++ b/erpnext/crm/doctype/social_media_post/test_social_media_post.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestSocialMediaPost(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py b/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
index 3f999c1..ff3163c 100644
--- a/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestTwitterSettings(unittest.TestCase):
pass
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.js b/erpnext/crm/doctype/twitter_settings/twitter_settings.js
index f6f431c..112f3d4 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.js
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.js
@@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on('Twitter Settings', {
- onload: function(frm){
+ onload: function(frm) {
if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret){
frappe.confirm(
__('Session not valid, Do you want to login?'),
@@ -14,10 +14,11 @@
}
);
}
+ frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/twitter-settings'>${__('Click here')}</a>`]));
},
- refresh: function(frm){
+ refresh: function(frm) {
let msg, color, flag=false;
- if (frm.doc.session_status == "Active"){
+ if (frm.doc.session_status == "Active") {
msg = __("Session Active");
color = 'green';
flag = true;
@@ -28,7 +29,7 @@
flag = true;
}
- if (flag){
+ if (flag) {
frm.dashboard.set_headline_alert(
`<div class="row">
<div class="col-xs-12">
@@ -38,7 +39,7 @@
);
}
},
- login: function(frm){
+ login: function(frm) {
if (frm.doc.consumer_key && frm.doc.consumer_secret){
frappe.dom.freeze();
frappe.call({
@@ -52,7 +53,7 @@
});
}
},
- after_save: function(frm){
+ after_save: function(frm) {
frm.trigger("login");
}
});
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.json b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
index 36776e5..8d05877 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.json
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
@@ -2,6 +2,7 @@
"actions": [],
"creation": "2020-01-30 10:29:08.562108",
"doctype": "DocType",
+ "documentation": "https://docs.erpnext.com/docs/user/manual/en/CRM/twitter-settings",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
@@ -77,7 +78,7 @@
"image_field": "profile_pic",
"issingle": 1,
"links": [],
- "modified": "2020-05-13 17:50:47.934776",
+ "modified": "2021-02-18 15:18:07.900031",
"modified_by": "Administrator",
"module": "CRM",
"name": "Twitter Settings",
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
index 1e1beab..0205cca 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
@@ -3,13 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, os, tweepy, json
+
+import json
+
+import frappe
+import tweepy
from frappe import _
from frappe.model.document import Document
+from frappe.utils import get_url_to_form
from frappe.utils.file_manager import get_file_path
-from frappe.utils import get_url_to_form, get_link_to_form
from tweepy.error import TweepError
+
class TwitterSettings(Document):
@frappe.whitelist()
def get_authorize_url(self):
@@ -32,7 +37,9 @@
try:
auth.get_access_token(oauth_verifier)
- api = self.get_api(auth.access_token, auth.access_token_secret)
+ self.access_token = auth.access_token
+ self.access_token_secret = auth.access_token_secret
+ api = self.get_api()
user = api.me()
profile_pic = (user._json["profile_image_url"]).replace("_normal","")
@@ -50,11 +57,11 @@
frappe.msgprint(_("Error! Failed to get access token."))
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
- def get_api(self, access_token, access_token_secret):
+ def get_api(self):
# authentication of consumer key and secret
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
# authentication of access token and secret
- auth.set_access_token(access_token, access_token_secret)
+ auth.set_access_token(self.access_token, self.access_token_secret)
return tweepy.API(auth)
@@ -68,13 +75,13 @@
def upload_image(self, media):
media = get_file_path(media)
- api = self.get_api(self.access_token, self.access_token_secret)
+ api = self.get_api()
media = api.media_upload(media)
return media.media_id
def send_tweet(self, text, media_id=None):
- api = self.get_api(self.access_token, self.access_token_secret)
+ api = self.get_api()
try:
if media_id:
response = api.update_status(status = text, media_ids = [media_id])
@@ -84,12 +91,32 @@
return response
except TweepError as e:
- content = json.loads(e.response.content)
- content = content["errors"][0]
- 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))
+ self.api_error(e)
+
+ def delete_tweet(self, tweet_id):
+ api = self.get_api()
+ try:
+ api.destroy_status(tweet_id)
+ except TweepError as e:
+ self.api_error(e)
+
+ def get_tweet(self, tweet_id):
+ api = self.get_api()
+ try:
+ response = api.get_status(tweet_id, trim_user=True, include_entities=True)
+ except TweepError as e:
+ self.api_error(e)
+
+ return response._json
+
+ def api_error(self, e):
+ content = json.loads(e.response.content)
+ content = content["errors"][0]
+ 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.whitelist(allow_guest=True)
def callback(oauth_token = None, oauth_verifier = None):
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
index f244daf..0da0e0e 100644
--- a/erpnext/crm/doctype/utils.py
+++ b/erpnext/crm/doctype/utils.py
@@ -1,6 +1,6 @@
+
import frappe
-from frappe import _
-import json
+
@frappe.whitelist()
def get_last_interaction(contact=None, lead=None):
diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js
index 0bc77a3..f29c2c6 100644
--- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js
+++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js
@@ -16,4 +16,3 @@
}
]
};
-
diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
index ec49883..87f516b 100644
--- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
+++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
columns=get_columns("Campaign Name")
@@ -132,4 +134,4 @@
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]
\ No newline at end of file
+ )""" % ', '.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 2ffbc3e..df57893 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
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute(filters=None):
columns = [
{
diff --git a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js
index 0325de9..eeb8984 100644
--- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js
+++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js
@@ -20,5 +20,3 @@
},
]
};
-
-
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 e66bc1e..71efdb9 100644
--- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
+++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, msgprint
from frappe.utils import date_diff, flt
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/crm/report/lead_details/lead_details.js b/erpnext/crm/report/lead_details/lead_details.js
index f92070d..2f6d242 100644
--- a/erpnext/crm/report/lead_details/lead_details.js
+++ b/erpnext/crm/report/lead_details/lead_details.js
@@ -49,4 +49,4 @@
"options": "Territory",
}
]
-};
\ No newline at end of file
+};
diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py
index eeaaec2..11e8276 100644
--- a/erpnext/crm/report/lead_details/lead_details.py
+++ b/erpnext/crm/report/lead_details/lead_details.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe import _
+
import frappe
+from frappe import _
+
def execute(filters=None):
columns, data = get_columns(), get_data(filters)
@@ -107,7 +109,7 @@
"options": "Country",
"width": 100
},
-
+
]
return columns
@@ -142,7 +144,7 @@
company = %(company)s
AND `tabLead`.creation BETWEEN %(from_date)s AND %(to_date)s
{conditions}
- ORDER BY
+ ORDER BY
`tabLead`.creation asc """.format(conditions=get_conditions(filters)), filters, as_dict=1)
def get_conditions(filters) :
@@ -153,6 +155,5 @@
if filters.get("status"):
conditions.append(" and `tabLead`.status=%(status)s")
-
- return " ".join(conditions) if conditions else ""
+ return " ".join(conditions) if conditions else ""
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 8fe16a2..5406eba 100644
--- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
+++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
+
from erpnext.crm.report.campaign_efficiency.campaign_efficiency import get_lead_data
+
def execute(filters=None):
columns, data = [], []
columns=get_columns()
diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.js b/erpnext/crm/report/lost_opportunity/lost_opportunity.js
index d79f8c8..97c56f8 100644
--- a/erpnext/crm/report/lost_opportunity/lost_opportunity.js
+++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.js
@@ -64,4 +64,4 @@
"options": "User"
},
]
-};
\ No newline at end of file
+};
diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.py b/erpnext/crm/report/lost_opportunity/lost_opportunity.py
index 1aa4afe..b308ceb 100644
--- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py
+++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe import _
+
import frappe
+from frappe import _
+
def execute(filters=None):
columns, data = get_columns(), get_data(filters)
@@ -87,17 +89,17 @@
`tabOpportunity`.sales_stage,
`tabOpportunity`.territory
FROM
- `tabOpportunity`
+ `tabOpportunity`
{join}
WHERE
`tabOpportunity`.status = 'Lost' and `tabOpportunity`.company = %(company)s
- AND `tabOpportunity`.modified BETWEEN %(from_date)s AND %(to_date)s
- {conditions}
- GROUP BY
- `tabOpportunity`.name
- ORDER BY
+ AND `tabOpportunity`.modified BETWEEN %(from_date)s AND %(to_date)s
+ {conditions}
+ GROUP BY
+ `tabOpportunity`.name
+ ORDER BY
`tabOpportunity`.creation asc """.format(conditions=get_conditions(filters), join=get_join(filters)), filters, as_dict=1)
-
+
def get_conditions(filters):
conditions = []
@@ -117,15 +119,15 @@
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
+ join = """LEFT JOIN `tabOpportunity Lost Reason Detail`
+ ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
`tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name"""
if filters.get("lost_reason"):
- join = """JOIN `tabOpportunity Lost Reason Detail`
- ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
+ join = """JOIN `tabOpportunity Lost Reason Detail`
+ 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"))
-
- return join
\ No newline at end of file
+
+ return join
diff --git a/erpnext/healthcare/doctype/prescription_dosage/__init__.py b/erpnext/crm/report/opportunity_summary_by_sales_stage/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/prescription_dosage/__init__.py
rename to erpnext/crm/report/opportunity_summary_by_sales_stage/__init__.py
diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
new file mode 100644
index 0000000..116db2f
--- /dev/null
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Opportunity Summary by Sales Stage"] = {
+ "filters": [
+ {
+ fieldname: "based_on",
+ label: __("Based On"),
+ fieldtype: "Select",
+ options: "Opportunity Owner\nSource\nOpportunity Type",
+ default: "Opportunity Owner"
+ },
+ {
+ fieldname: "data_based_on",
+ label: __("Data Based On"),
+ fieldtype: "Select",
+ options: "Number\nAmount",
+ default: "Number"
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ },
+ {
+ fieldname: "status",
+ label: __("Status"),
+ fieldtype: "MultiSelectList",
+ get_data: function() {
+ return [
+ {value: "Open", description: "Status"},
+ {value: "Converted", description: "Status"},
+ {value: "Quotation", description: "Status"},
+ {value: "Replied", description: "Status"}
+ ]
+ }
+ },
+ {
+ fieldname: "opportunity_source",
+ label: __("Oppoturnity Source"),
+ fieldtype: "Link",
+ options: "Lead Source",
+ },
+ {
+ fieldname: "opportunity_type",
+ label: __("Opportunity Type"),
+ fieldtype: "Link",
+ options: "Opportunity Type",
+ },
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company")
+ }
+ ]
+};
\ No newline at end of file
diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.json b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.json
new file mode 100644
index 0000000..3605aec
--- /dev/null
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-07-28 12:18:24.028737",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-07-28 12:18:24.028737",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Opportunity Summary by Sales Stage",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Opportunity",
+ "report_name": "Opportunity Summary by Sales Stage ",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Sales User"
+ },
+ {
+ "role": "Sales Manager"
+ }
+ ]
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..4cff13f
--- /dev/null
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py
@@ -0,0 +1,254 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+import json
+
+import frappe
+import pandas
+from frappe import _
+from frappe.utils import flt
+from six import iteritems
+
+from erpnext.setup.utils import get_exchange_rate
+
+
+def execute(filters=None):
+ return OpportunitySummaryBySalesStage(filters).run()
+
+class OpportunitySummaryBySalesStage(object):
+ def __init__(self,filters=None):
+ self.filters = frappe._dict(filters or {})
+
+ def run(self):
+ self.get_columns()
+ self.get_data()
+ self.get_chart_data()
+ return self.columns, self.data, None, self.chart
+
+ 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') == '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
+ })
+
+ self.set_sales_stage_columns()
+
+ def set_sales_stage_columns(self):
+ 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
+ })
+
+ 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')]
+
+ 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',
+ filters=self.get_conditions(),
+ 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',
+ filters=self.get_conditions(),
+ 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()
+
+ 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]
+ })
+
+ self.query_result = self.grouped_data
+
+ def get_rows(self):
+ self.data = []
+ self.get_formatted_data()
+
+ for based_on,data in iteritems(self.formatted_data):
+ 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')
+ row[sales_stage] = data.get(sales_stage)
+
+ self.data.append(row)
+
+ def get_formatted_data(self):
+ self.formatted_data = frappe._dict()
+
+ for d in self.query_result:
+ 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')]
+
+ 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')
+ count = d.get(data_based_on)
+
+ if assignments:
+ if len(assignments) > 1:
+ for assigned_to in assignments:
+ self.set_formatted_data_based_on_sales_stage(assigned_to, sales_stage, count)
+ else:
+ assigned_to = assignments[0]
+ 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')
+ count = d.get(data_based_on)
+ self.set_formatted_data_based_on_sales_stage(value, sales_stage, count)
+
+ def set_formatted_data_based_on_sales_stage(self, based_on, sales_stage, count):
+ self.formatted_data.setdefault(based_on, frappe._dict()).setdefault(sales_stage, 0)
+ self.formatted_data[based_on][sales_stage] += count
+
+ def get_conditions(self):
+ filters = []
+
+ 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_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('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
+
+ def get_chart_data(self):
+ labels = []
+ datasets = []
+ values = [0] * 8
+
+ for sales_stage in self.sales_stage_list:
+ labels.append(sales_stage)
+
+ 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]:
+ values[count] = values[count] + data[options]
+
+ datasets.append({'name':options, 'values':values})
+
+ self.chart = {
+ 'data':{
+ 'labels': labels,
+ 'datasets': datasets
+ },
+ 'type':'line'
+ }
+
+ 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'))
+
+ else:
+ 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')
+
+ 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
\ No newline at end of file
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
new file mode 100644
index 0000000..13859d9
--- /dev/null
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/test_opportunity_summary_by_sales_stage.py
@@ -0,0 +1,94 @@
+import unittest
+
+import frappe
+
+from erpnext.crm.report.opportunity_summary_by_sales_stage.opportunity_summary_by_sales_stage import (
+ execute,
+)
+from erpnext.crm.report.sales_pipeline_analytics.test_sales_pipeline_analytics import (
+ create_company,
+ create_customer,
+ create_opportunity,
+)
+
+
+class TestOpportunitySummaryBySalesStage(unittest.TestCase):
+ @classmethod
+ def setUpClass(self):
+ frappe.db.delete("Opportunity")
+ create_company()
+ create_customer()
+ create_opportunity()
+
+ def test_opportunity_summary_by_sales_stage(self):
+ self.check_for_opportunity_owner()
+ self.check_for_source()
+ self.check_for_opportunity_type()
+ self.check_all_filters()
+
+ def check_for_opportunity_owner(self):
+ filters = {
+ 'based_on': "Opportunity Owner",
+ 'data_based_on': "Number",
+ 'company': "Best Test"
+ }
+
+ report = execute(filters)
+
+ 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"
+ }
+
+ report = execute(filters)
+
+ 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"
+ }
+
+ report = execute(filters)
+
+ 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"]
+ }
+
+ report = execute(filters)
+
+ expected_data = [{
+ 'opportunity_type': 'Sales',
+ 'Prospecting': 1
+ }]
+
+ self.assertEqual(expected_data, report[1])
\ No newline at end of file
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 3a9d57d..4a16f3d 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
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import add_days, now
+
def execute(filters=None):
columns, data = [], []
set_defaut_value_for_filters(filters)
@@ -106,4 +108,4 @@
return lead_filters
def get_creation_date_based_on_lead_age(filters):
- return add_days(now(), (filters.get('lead_age') * -1))
\ No newline at end of file
+ return add_days(now(), (filters.get('lead_age') * -1))
diff --git a/erpnext/healthcare/report/patient_appointment_analytics/__init__.py b/erpnext/crm/report/sales_pipeline_analytics/__init__.py
similarity index 100%
rename from erpnext/healthcare/report/patient_appointment_analytics/__init__.py
rename to erpnext/crm/report/sales_pipeline_analytics/__init__.py
diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js
new file mode 100644
index 0000000..1426f4b
--- /dev/null
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js
@@ -0,0 +1,70 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Sales Pipeline Analytics"] = {
+ "filters": [
+ {
+ fieldname: "pipeline_by",
+ label: __("Pipeline By"),
+ fieldtype: "Select",
+ options: "Owner\nSales Stage",
+ default: "Owner"
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date"
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date"
+ },
+ {
+ fieldname: "range",
+ label: __("Range"),
+ fieldtype: "Select",
+ options: "Monthly\nQuarterly",
+ default: "Monthly"
+ },
+ {
+ fieldname: "assigned_to",
+ label: __("Assigned To"),
+ fieldtype: "Link",
+ options: "User"
+ },
+ {
+ fieldname: "status",
+ label: __("Status"),
+ fieldtype: "Select",
+ options: "Open\nQuotation\nConverted\nReplied"
+ },
+ {
+ fieldname: "based_on",
+ label: __("Based On"),
+ fieldtype: "Select",
+ options: "Number\nAmount",
+ default: "Number"
+ },
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company")
+ },
+ {
+ fieldname: "opportunity_source",
+ label: __("Opportunity Source"),
+ fieldtype: "Link",
+ options: "Lead Source"
+ },
+ {
+ fieldname: "opportunity_type",
+ label: __("Opportunity Type"),
+ fieldtype: "Link",
+ options: "Opportunity Type"
+ },
+ ]
+};
diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.json b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.json
new file mode 100644
index 0000000..cffdddf
--- /dev/null
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-07-01 17:29:09.530787",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-07-01 17:45:17.612861",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Sales Pipeline Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Opportunity",
+ "report_name": "Sales Pipeline Analytics",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Sales User"
+ },
+ {
+ "role": "Sales Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
new file mode 100644
index 0000000..7466982
--- /dev/null
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
@@ -0,0 +1,333 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import json
+from datetime import date
+
+import frappe
+import pandas
+from dateutil.relativedelta import relativedelta
+from frappe import _
+from frappe.utils import cint, flt
+from six import iteritems
+
+from erpnext.setup.utils import get_exchange_rate
+
+
+def execute(filters=None):
+ return SalesPipelineAnalytics(filters).run()
+
+class SalesPipelineAnalytics(object):
+ def __init__(self, filters=None):
+ self.filters = frappe._dict(filters or {})
+
+ def run(self):
+ self.get_columns()
+ self.get_data()
+ self.get_chart_data()
+
+ return self.columns, self.data, None, self.chart
+
+ def get_columns(self):
+ self.columns = []
+
+ self.set_range_columns()
+ self.set_pipeline_based_on_column()
+
+ def set_range_columns(self):
+ based_on = {
+ 'Number': 'Int',
+ 'Amount': 'Currency'
+ }[self.filters.get('based_on')]
+
+ 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
+ })
+
+ 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
+ })
+
+ 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
+ })
+
+ 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.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_period = {
+ '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.duration = {
+ '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')]
+
+ def get_data(self):
+ self.get_fields()
+
+ 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
+ )
+
+ 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']
+ )
+
+ 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]
+ })
+
+ self.query_result = self.grouped_data
+
+ self.get_periodic_data()
+ self.append_data(self.pipeline_by, self.period_by)
+
+ def get_conditions(self):
+ conditions = []
+
+ 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('status'):
+ conditions.append({'status': self.filters.get('status')})
+
+ 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')]])
+
+ return conditions
+
+ def get_chart_data(self):
+ labels = []
+ datasets = []
+
+ self.append_to_dataset(datasets)
+
+ for column in self.columns:
+ if column['fieldname'] != 'opportunity_owner' and column['fieldname'] != 'sales_stage':
+ labels.append(column['fieldname'])
+
+ 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')]
+
+ pipeline_by = {
+ 'Owner': 'opportunity_owner',
+ 'Sales Stage': 'sales_stage'
+ }[self.filters.get('pipeline_by')]
+
+ frequency = {
+ 'Monthly': 'month',
+ 'Quarterly': 'quarter'
+ }[self.filters.get('range')]
+
+ for info in self.query_result:
+ if self.filters.get('range') == 'Monthly':
+ period = info.get(frequency)
+ 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']
+ else:
+ assigned_to = json.loads(value)
+ self.check_for_assigned_to(period, value, count_or_amount, assigned_to, info)
+
+ else:
+ self.set_formatted_data(period, value, count_or_amount, None)
+
+ 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'):
+ for user in assigned_to:
+ 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
+ else:
+ for user in assigned_to:
+ value = user
+ self.periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0)
+ self.periodic_data[value][period] += count_or_amount
+ else:
+ value = assigned_to[0]
+ self.periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0)
+ self.periodic_data[value][period] += count_or_amount
+
+ else:
+ self.periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0)
+ 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'):
+ 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= []
+ current_date = date.today()
+ month_number = date.today().month
+
+ 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')]
+
+ based_on = {
+ 'Amount': 'amount',
+ 'Number': 'count'
+ }[self.filters.get('based_on')]
+
+ if self.filters.get('range') == 'Quarterly':
+ frequency_list = [1,2,3,4]
+ count = [0] * 4
+
+ if self.filters.get('range') == 'Monthly':
+ frequency_list = self.get_month_list()
+ count = [0] * 12
+
+ for info in self.query_result:
+ 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})
+
+ def append_data(self, pipeline_by, period_by):
+ self.data = []
+ for pipeline,period_data in iteritems(self.periodic_data):
+ row = {pipeline_by : pipeline}
+ for info in self.query_result:
+ 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))}'
+
+ 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'])
+
+ 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'))
+
+ else:
+ value = get_exchange_rate(from_currency, to_currency)
+ cacheobj.set(from_currency, value)
+ 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
\ No newline at end of file
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
new file mode 100644
index 0000000..24c3839
--- /dev/null
+++ b/erpnext/crm/report/sales_pipeline_analytics/test_sales_pipeline_analytics.py
@@ -0,0 +1,238 @@
+import unittest
+
+import frappe
+
+from erpnext.crm.report.sales_pipeline_analytics.sales_pipeline_analytics import execute
+
+
+class TestSalesPipelineAnalytics(unittest.TestCase):
+ @classmethod
+ def setUpClass(self):
+ frappe.db.delete("Opportunity")
+ create_company()
+ create_customer()
+ create_opportunity()
+
+ def test_sales_pipeline_analytics(self):
+ self.check_for_monthly_and_number()
+ self.check_for_monthly_and_amount()
+ self.check_for_quarterly_and_number()
+ self.check_for_quarterly_and_amount()
+ self.check_for_all_filters()
+
+ def check_for_monthly_and_number(self):
+ filters = {
+ '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
+ }
+ ]
+
+ self.assertEqual(expected_data,report[1])
+
+ filters = {
+ '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
+ }
+ ]
+
+ 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"
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ 'opportunity_owner':'Not Assigned',
+ 'August':150000
+ }
+ ]
+
+ self.assertEqual(expected_data,report[1])
+
+ filters = {
+ '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
+ }
+ ]
+
+ 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"
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ 'opportunity_owner':'Not Assigned',
+ 'Q3':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"
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ 'sales_stage':'Prospecting',
+ 'Q3':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"
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ 'opportunity_owner':'Not Assigned',
+ 'Q3':150000
+ }
+ ]
+
+ self.assertEqual(expected_data,report[1])
+
+ filters = {
+ '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
+ }
+ ]
+
+ 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'
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ 'opportunity_owner':'Not Assigned',
+ 'August': 1
+ }
+ ]
+
+ self.assertEqual(expected_data,report[1])
+
+def create_company():
+ doc = frappe.db.exists('Company','Best Test')
+ if not doc:
+ 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")
+ if not doc:
+ doc = frappe.new_doc("Customer")
+ doc.customer_name = '_Test NC'
+ doc.insert()
+
+def create_opportunity():
+ 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'])
+ 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
diff --git a/erpnext/crm/workspace/crm/crm.json b/erpnext/crm/workspace/crm/crm.json
index c363395..a661b62 100644
--- a/erpnext/crm/workspace/crm/crm.json
+++ b/erpnext/crm/workspace/crm/crm.json
@@ -148,6 +148,24 @@
"type": "Link"
},
{
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Sales Pipeline Analytics",
+ "link_to": "Sales Pipeline Analytics",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Opportunity Summary by Sales Stage",
+ "link_to": "Opportunity Summary by Sales Stage",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
@@ -403,7 +421,7 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:56.913091",
+ "modified": "2021-08-19 19:08:08.728876",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",
diff --git a/erpnext/demo/demo.py b/erpnext/demo/demo.py
index e89b689..bd744b2 100644
--- a/erpnext/demo/demo.py
+++ b/erpnext/demo/demo.py
@@ -1,11 +1,16 @@
from __future__ import unicode_literals
-import frappe, sys
-import erpnext
+import sys
+
+import frappe
import frappe.utils
-from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset
+
+import erpnext
+from erpnext.demo.setup import education, manufacture, retail, setup_data
+from erpnext.demo.user import accounts
from erpnext.demo.user import education as edu
-from erpnext.demo.setup import education, manufacture, setup_data, healthcare, retail
+from erpnext.demo.user import fixed_asset, hr, manufacturing, projects, purchase, sales, stock
+
"""
Make a demo
@@ -33,8 +38,6 @@
retail.setup_data()
elif domain== 'Education':
education.setup_data()
- elif domain== 'Healthcare':
- healthcare.setup_data()
site = frappe.local.site
frappe.destroy()
@@ -88,7 +91,7 @@
elif domain=='Education':
edu.work()
- except:
+ except Exception:
frappe.db.set_global('demo_last_date', current_date)
raise
finally:
diff --git a/erpnext/demo/domains.py b/erpnext/demo/domains.py
index d5c2bfd..7f48b92 100644
--- a/erpnext/demo/domains.py
+++ b/erpnext/demo/domains.py
@@ -16,13 +16,10 @@
'Education': {
'company_name': 'Whitmore College'
},
- 'Healthcare': {
- 'company_name': 'ABC Hospital Ltd.'
- },
'Agriculture': {
'company_name': 'Schrute Farms'
},
'Non Profit': {
'company_name': 'Erpnext Foundation'
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/demo/setup/education.py b/erpnext/demo/setup/education.py
index cf9451d..304bc3d 100644
--- a/erpnext/demo/setup/education.py
+++ b/erpnext/demo/setup/education.py
@@ -2,11 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.utils.make_random import get_random
-from datetime import datetime
-from erpnext.demo.setup.setup_data import import_json
+import json
import random
+from datetime import datetime
+
+import frappe
+from frappe.utils.make_random import get_random
+
+from erpnext.demo.setup.setup_data import import_json
+
def setup_data():
frappe.flags.mute_emails = True
diff --git a/erpnext/demo/setup/healthcare.py b/erpnext/demo/setup/healthcare.py
deleted file mode 100644
index aa389e5..0000000
--- a/erpnext/demo/setup/healthcare.py
+++ /dev/null
@@ -1,167 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-
-import frappe, json
-from frappe.utils.make_random import get_random
-import datetime
-from erpnext.demo.setup.setup_data import import_json
-from frappe.utils import getdate
-from erpnext.healthcare.doctype.lab_test.lab_test import create_test_from_template
-
-def setup_data():
- frappe.flags.mute_emails = True
- make_masters()
- make_patient()
- make_lab_test()
- make_consulation()
- make_appointment()
- consulation_on_appointment()
- lab_test_on_encounter()
- frappe.db.commit()
- frappe.clear_cache()
-
-def make_masters():
- import_json("Healthcare Practitioner")
- import_drug()
- frappe.db.commit()
-
-def make_patient():
- file_path = get_json_path("Patient")
- with open(file_path, "r") as open_file:
- patient_data = json.loads(open_file.read())
- count = 1
-
- for d in enumerate(patient_data):
- patient = frappe.new_doc("Patient")
- patient.patient_name = d[1]['patient_name'].title()
- patient.sex = d[1]['gender']
- patient.blood_group = "A Positive"
- patient.date_of_birth = datetime.datetime(1990, 3, 25)
- patient.email_id = d[1]['patient_name'] + "_" + patient.date_of_birth.strftime('%m/%d/%Y') + "@example.com"
- if count <5:
- patient.insert()
- frappe.db.commit()
- count+=1
-
-def make_appointment():
- i = 1
- while i <= 4:
- practitioner = get_random("Healthcare Practitioner")
- department = frappe.get_value("Healthcare Practitioner", practitioner, "department")
- patient = get_random("Patient")
- patient_sex = frappe.get_value("Patient", patient, "sex")
- appointment = frappe.new_doc("Patient Appointment")
- startDate = datetime.datetime.now()
- for x in random_date(startDate,0):
- appointment_datetime = x
- appointment.appointment_datetime = appointment_datetime
- appointment.appointment_time = appointment_datetime
- appointment.appointment_date = appointment_datetime
- appointment.patient = patient
- appointment.patient_sex = patient_sex
- appointment.practitioner = practitioner
- appointment.department = department
- appointment.save(ignore_permissions = True)
- i += 1
-
-def make_consulation():
- for i in range(3):
- practitioner = get_random("Healthcare Practitioner")
- department = frappe.get_value("Healthcare Practitioner", practitioner, "department")
- patient = get_random("Patient")
- patient_sex = frappe.get_value("Patient", patient, "sex")
- encounter = set_encounter(patient, patient_sex, practitioner, department, getdate(), i)
- encounter.save(ignore_permissions=True)
-
-def consulation_on_appointment():
- for i in range(3):
- appointment = get_random("Patient Appointment")
- appointment = frappe.get_doc("Patient Appointment",appointment)
- encounter = set_encounter(appointment.patient, appointment.patient_sex, appointment.practitioner, appointment.department, appointment.appointment_date, i)
- encounter.appointment = appointment.name
- encounter.save(ignore_permissions=True)
-
-def set_encounter(patient, patient_sex, practitioner, department, encounter_date, i):
- encounter = frappe.new_doc("Patient Encounter")
- encounter.patient = patient
- encounter.patient_sex = patient_sex
- encounter.practitioner = practitioner
- encounter.visit_department = department
- encounter.encounter_date = encounter_date
- if i > 2 and patient_sex=='Female':
- encounter.symptoms = "Having chest pains for the last week."
- encounter.diagnosis = """This patient's description of dull, aching,
- exertion related substernal chest pain is suggestive of ischemic
- cardiac origin. Her findings of a FH of early ASCVD, hypertension,
- and early surgical menopause are pertinent risk factors for development
- of coronary artery disease. """
- else:
- encounter = append_drug_rx(encounter)
- encounter = append_test_rx(encounter)
- return encounter
-
-def make_lab_test():
- practitioner = get_random("Healthcare Practitioner")
- patient = get_random("Patient")
- patient_sex = frappe.get_value("Patient", patient, "sex")
- template = get_random("Lab Test Template")
- set_lab_test(patient, patient_sex, practitioner, template)
-
-def lab_test_on_encounter():
- i = 1
- while i <= 2:
- test_rx = get_random("Lab Prescription", filters={'test_created': 0})
- test_rx = frappe.get_doc("Lab Prescription", test_rx)
- encounter = frappe.get_doc("Patient Encounter", test_rx.parent)
- set_lab_test(encounter.patient, encounter.patient_sex, encounter.practitioner, test_rx.test_code, test_rx.name)
- i += 1
-
-def set_lab_test(patient, patient_sex, practitioner, template, rx=None):
- lab_test = frappe.new_doc("Lab Test")
- lab_test.practitioner = practitioner
- lab_test.patient = patient
- lab_test.patient_sex = patient_sex
- lab_test.template = template
- lab_test.prescription = rx
- create_test_from_template(lab_test)
-
-def append_test_rx(encounter):
- i = 1
- while i <= 2:
- test_rx = encounter.append("test_prescription")
- test_rx.test_code = get_random("Lab Test Template")
- i += 1
- return encounter
-
-def append_drug_rx(encounter):
- i = 1
- while i <= 3:
- drug = get_random("Item", filters={"item_group":"Drug"})
- drug = frappe.get_doc("Item", drug)
- drug_rx = encounter.append("drug_prescription")
- drug_rx.drug_code = drug.item_code
- drug_rx.drug_name = drug.item_name
- drug_rx.dosage = get_random("Prescription Dosage")
- drug_rx.period = get_random("Prescription Duration")
- i += 1
- return encounter
-
-def random_date(start,l):
- current = start
- while l >= 0:
- curr = current + datetime.timedelta(minutes=60)
- yield curr
- l-=1
-
-def import_drug():
- frappe.flags.in_import = True
- data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'drug_list.json')).read())
- for d in data:
- doc = frappe.new_doc("Item")
- doc.update(d)
- doc.insert()
- frappe.flags.in_import = False
-
-def get_json_path(doctype):
- return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json')
diff --git a/erpnext/demo/setup/manufacture.py b/erpnext/demo/setup/manufacture.py
index 7d6b501..5db3519 100644
--- a/erpnext/demo/setup/manufacture.py
+++ b/erpnext/demo/setup/manufacture.py
@@ -1,13 +1,16 @@
from __future__ import unicode_literals
-import random, json
-import frappe
-from frappe.utils import nowdate, add_days
-from erpnext.demo.setup.setup_data import import_json
-from erpnext.demo.domains import data
+import json
+import random
+import frappe
+from frappe.utils import add_days, nowdate
from six import iteritems
+from erpnext.demo.domains import data
+from erpnext.demo.setup.setup_data import import_json
+
+
def setup_data():
import_json("Location")
import_json("Asset Category")
diff --git a/erpnext/demo/setup/retail.py b/erpnext/demo/setup/retail.py
index 82d1c15..d94d2d6 100644
--- a/erpnext/demo/setup/retail.py
+++ b/erpnext/demo/setup/retail.py
@@ -1,12 +1,12 @@
from __future__ import unicode_literals
-import random, json
+import json
+
import frappe
-from frappe.utils import nowdate, add_days
-from erpnext.demo.setup.setup_data import import_json
+from six import iteritems
+
from erpnext.demo.domains import data
-from six import iteritems
def setup_data():
setup_item()
diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py
index 05ee28a..af53043 100644
--- a/erpnext/demo/setup/setup_data.py
+++ b/erpnext/demo/setup/setup_data.py
@@ -1,13 +1,18 @@
from __future__ import print_function, unicode_literals
-import random, json
-import frappe, erpnext
-from frappe.utils.nestedset import get_root_of
-from frappe.utils import flt, now_datetime, cstr, random_string
-from frappe.utils.make_random import add_random_children, get_random
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from erpnext.demo.domains import data
+import json
+import random
+
+import frappe
from frappe import _
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from frappe.utils import cstr, flt, now_datetime, random_string
+from frappe.utils.make_random import add_random_children, get_random
+from frappe.utils.nestedset import get_root_of
+
+import erpnext
+from erpnext.demo.domains import data
+
def setup(domain):
frappe.flags.in_demo = 1
@@ -191,10 +196,6 @@
'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager',
'Support Team')
- if domain == "Healthcare":
- user.add_roles('Physician', 'Healthcare Administrator', 'Laboratory User',
- 'Nursing User', 'Patient')
-
if domain == "Education":
user.add_roles('Academics User')
diff --git a/erpnext/demo/user/accounts.py b/erpnext/demo/user/accounts.py
index 7fab772..2a24824 100644
--- a/erpnext/demo/user/accounts.py
+++ b/erpnext/demo/user/accounts.py
@@ -4,19 +4,25 @@
from __future__ import unicode_literals
-import erpnext
-import frappe
import random
-from frappe.utils import random_string
+
+import frappe
from frappe.desk import query_report
+from frappe.utils import random_string
+from frappe.utils.make_random import get_random
+
+import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry_against_invoice
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-from frappe.utils.make_random import get_random
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request, make_payment_entry
+from erpnext.accounts.doctype.payment_request.payment_request import (
+ make_payment_entry,
+ make_payment_request,
+)
from erpnext.demo.user.sales import make_sales_order
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+
def work():
frappe.set_user(frappe.db.get_global('demo_accounts_user'))
diff --git a/erpnext/demo/user/education.py b/erpnext/demo/user/education.py
index fc31176..adc0463 100644
--- a/erpnext/demo/user/education.py
+++ b/erpnext/demo/user/education.py
@@ -4,13 +4,21 @@
from __future__ import unicode_literals
-import frappe
import random
+from datetime import timedelta
+
+import frappe
from frappe.utils import cstr
from frappe.utils.make_random import get_random
-from datetime import timedelta
-from erpnext.education.api import get_student_group_students, make_attendance_records, enroll_student, \
- get_fee_schedule, collect_fees, get_course
+
+from erpnext.education.api import (
+ collect_fees,
+ enroll_student,
+ get_course,
+ get_fee_schedule,
+ get_student_group_students,
+ make_attendance_records,
+)
def work():
@@ -19,7 +27,7 @@
approve_random_student_applicant()
enroll_random_student(frappe.flags.current_date)
# if frappe.flags.current_date.weekday()== 0:
- # make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5))
+ # make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5))
mark_student_attendance(frappe.flags.current_date)
# make_assessment_plan()
make_fees()
@@ -48,7 +56,7 @@
frappe.db.commit()
assign_student_group(enrollment.student, enrollment.student_name, enrollment.program,
enrolled_courses, enrollment.student_batch_name)
-
+
def assign_student_group(student, student_name, program, courses, batch):
course_list = [d["course"] for d in courses]
for d in frappe.get_list("Student Group", fields=("name"), filters={"program": program, "course":("in", course_list), "disabled": 0}):
@@ -69,11 +77,11 @@
students = get_student_group_students(d.name)
for stud in students:
make_attendance_records(stud.student, stud.student_name, status[weighted_choice([9,4])], None, d.name, current_date)
-
+
def make_fees():
for d in range(1,10):
random_fee = get_random("Fees", {"paid_amount": 0})
- collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount"))
+ collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount"))
def make_assessment_plan(date):
for d in range(1,4):
@@ -84,7 +92,7 @@
doc.assessment_group = get_random("Assessment Group", {"is_group": 0, "parent": "2017-18 (Semester 2)"})
doc.grading_scale = get_random("Grading Scale")
doc.maximum_assessment_score = 100
-
+
def make_course_schedule(start_date, end_date):
for d in frappe.db.get_list("Student Group"):
cs = frappe.new_doc("Scheduling Tool")
@@ -114,4 +122,4 @@
rnd = random.random() * running_total
for i, total in enumerate(totals):
if rnd < total:
- return i
\ No newline at end of file
+ return i
diff --git a/erpnext/demo/user/fixed_asset.py b/erpnext/demo/user/fixed_asset.py
index dc094e1..ec0e4c8 100644
--- a/erpnext/demo/user/fixed_asset.py
+++ b/erpnext/demo/user/fixed_asset.py
@@ -6,6 +6,7 @@
import frappe
from frappe.utils.make_random import get_random
+
from erpnext.assets.doctype.asset.asset import make_sales_invoice
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset
diff --git a/erpnext/demo/user/hr.py b/erpnext/demo/user/hr.py
index 0211bc8..17d5829 100644
--- a/erpnext/demo/user/hr.py
+++ b/erpnext/demo/user/hr.py
@@ -1,15 +1,23 @@
from __future__ import unicode_literals
-import frappe, erpnext
-import random
+
import datetime
-from frappe.utils import random_string, add_days, get_last_day, getdate
+import random
+
+import frappe
+from frappe.utils import add_days, get_last_day, getdate, random_string
+from frappe.utils.make_random import get_random
+
+import erpnext
+from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
+from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
+from erpnext.hr.doctype.leave_application.leave_application import (
+ AttendanceAlreadyMarkedError,
+ OverlapError,
+ get_leave_balance_on,
+)
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
-from frappe.utils.make_random import get_random
-from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
-from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
-from erpnext.hr.doctype.leave_application.leave_application import (get_leave_balance_on,
- OverlapError, AttendanceAlreadyMarkedError)
+
def work():
frappe.set_user(frappe.db.get_global('demo_hr_user'))
diff --git a/erpnext/demo/user/manufacturing.py b/erpnext/demo/user/manufacturing.py
index bece079..6e01f0f 100644
--- a/erpnext/demo/user/manufacturing.py
+++ b/erpnext/demo/user/manufacturing.py
@@ -3,21 +3,23 @@
from __future__ import unicode_literals
-import frappe, random, erpnext
+import random
from datetime import timedelta
-from frappe.utils.make_random import how_many
+
+import frappe
from frappe.desk import query_report
-from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
+from frappe.utils.make_random import how_many
+
+import erpnext
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
+
def work():
if random.random() < 0.3: return
frappe.set_user(frappe.db.get_global('demo_manufacturing_user'))
if not frappe.get_all('Sales Order'): return
- from erpnext.projects.doctype.timesheet.timesheet import OverlapError
-
ppt = frappe.new_doc("Production Plan")
ppt.company = erpnext.get_default_company()
# ppt.use_multi_level_bom = 1 #refactored
@@ -68,9 +70,12 @@
def make_stock_entry_from_pro(pro_id, purpose):
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
+ from erpnext.stock.doctype.stock_entry.stock_entry import (
+ DuplicateEntryForWorkOrderError,
+ IncorrectValuationRateError,
+ OperationsNotCompleteError,
+ )
from erpnext.stock.stock_ledger import NegativeStockError
- from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \
- DuplicateEntryForWorkOrderError, OperationsNotCompleteError
try:
st = frappe.get_doc(make_stock_entry(pro_id, purpose))
diff --git a/erpnext/demo/user/projects.py b/erpnext/demo/user/projects.py
index 044e296..b6b99de 100644
--- a/erpnext/demo/user/projects.py
+++ b/erpnext/demo/user/projects.py
@@ -3,11 +3,14 @@
from __future__ import unicode_literals
-import frappe, erpnext
+import frappe
from frappe.utils import flt
from frappe.utils.make_random import get_random
-from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
+
+import erpnext
from erpnext.demo.user.hr import make_sales_invoice_for_timesheet
+from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
+
def run_projects(current_date):
frappe.set_user(frappe.db.get_global('demo_projects_user'))
diff --git a/erpnext/demo/user/purchase.py b/erpnext/demo/user/purchase.py
index b7aca79..ec32f97 100644
--- a/erpnext/demo/user/purchase.py
+++ b/erpnext/demo/user/purchase.py
@@ -3,15 +3,22 @@
from __future__ import unicode_literals
-import frappe, random, json, erpnext
-from frappe.utils.make_random import how_many, get_random
+import json
+import random
+
+import frappe
from frappe.desk import query_report
-from erpnext.setup.utils import get_exchange_rate
+from frappe.utils.make_random import get_random, how_many
+
+import erpnext
from erpnext.accounts.party import get_party_account_currency
+from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
+ make_supplier_quotation_from_rfq,
+)
from erpnext.exceptions import InvalidCurrency
+from erpnext.setup.utils import get_exchange_rate
from erpnext.stock.doctype.material_request.material_request import make_request_for_quotation
-from erpnext.buying.doctype.request_for_quotation.request_for_quotation import \
- make_supplier_quotation_from_rfq
+
def work():
frappe.set_user(frappe.db.get_global('demo_purchase_user'))
diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py
index 8d5ba28..95494ee 100644
--- a/erpnext/demo/user/sales.py
+++ b/erpnext/demo/user/sales.py
@@ -3,12 +3,20 @@
from __future__ import unicode_literals
-import frappe, random, erpnext
+import random
+
+import frappe
from frappe.utils import flt
from frappe.utils.make_random import add_random_children, get_random
-from erpnext.setup.utils import get_exchange_rate
+
+import erpnext
+from erpnext.accounts.doctype.payment_request.payment_request import (
+ make_payment_entry,
+ make_payment_request,
+)
from erpnext.accounts.party import get_party_account_currency
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request, make_payment_entry
+from erpnext.setup.utils import get_exchange_rate
+
def work(domain="Manufacturing"):
frappe.set_user(frappe.db.get_global('demo_sales_user_2'))
diff --git a/erpnext/demo/user/stock.py b/erpnext/demo/user/stock.py
index d44da7d..188fc13 100644
--- a/erpnext/demo/user/stock.py
+++ b/erpnext/demo/user/stock.py
@@ -3,13 +3,18 @@
from __future__ import print_function, unicode_literals
-import frappe, random, erpnext
+import random
+
+import frappe
from frappe.desk import query_report
-from erpnext.stock.stock_ledger import NegativeStockError
-from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError
+
+import erpnext
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
+from erpnext.stock.doctype.serial_no.serial_no import SerialNoQtyError, SerialNoRequiredError
+from erpnext.stock.stock_ledger import NegativeStockError
+
def work():
frappe.set_user(frappe.db.get_global('demo_manufacturing_user'))
@@ -66,8 +71,10 @@
def make_stock_reconciliation():
# random set some items as damaged
- from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \
- import OpeningEntryAccountError, EmptyStockReconciliationItemsError
+ from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
+ EmptyStockReconciliationItemsError,
+ OpeningEntryAccountError,
+ )
if random.random() < 0.4:
stock_reco = frappe.new_doc("Stock Reconciliation")
@@ -88,8 +95,11 @@
frappe.db.rollback()
def submit_draft_stock_entries():
- from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \
- DuplicateEntryForWorkOrderError, OperationsNotCompleteError
+ from erpnext.stock.doctype.stock_entry.stock_entry import (
+ DuplicateEntryForWorkOrderError,
+ IncorrectValuationRateError,
+ OperationsNotCompleteError,
+ )
# try posting older drafts (if exists)
frappe.db.commit()
diff --git a/erpnext/domains/agriculture.py b/erpnext/domains/agriculture.py
index 8c7427a..9212d2e 100644
--- a/erpnext/domains/agriculture.py
+++ b/erpnext/domains/agriculture.py
@@ -25,4 +25,4 @@
],
'default_portal_role': 'System Manager',
'on_setup': 'erpnext.agriculture.setup.setup_agriculture'
-}
\ No newline at end of file
+}
diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py
index bbaa6e5..870624a 100644
--- a/erpnext/domains/education.py
+++ b/erpnext/domains/education.py
@@ -26,4 +26,4 @@
],
'on_setup': 'erpnext.education.setup.setup_education'
-}
\ No newline at end of file
+}
diff --git a/erpnext/domains/healthcare.py b/erpnext/domains/healthcare.py
deleted file mode 100644
index bbeb2c6..0000000
--- a/erpnext/domains/healthcare.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from __future__ import unicode_literals
-
-data = {
- 'desktop_icons': [
- 'Patient',
- 'Patient Appointment',
- 'Patient Encounter',
- 'Lab Test',
- 'Healthcare',
- 'Vital Signs',
- 'Clinical Procedure',
- 'Inpatient Record',
- 'Accounts',
- 'Buying',
- 'Stock',
- 'HR',
- 'ToDo'
- ],
- 'default_portal_role': 'Patient',
- 'restricted_roles': [
- 'Healthcare Administrator',
- 'LabTest Approver',
- 'Laboratory User',
- 'Nursing User',
- 'Physician',
- 'Patient'
- ],
- 'custom_fields': {
- 'Sales Invoice': [
- {
- 'fieldname': 'patient', 'label': 'Patient', 'fieldtype': 'Link', 'options': 'Patient',
- 'insert_after': 'naming_series'
- },
- {
- 'fieldname': 'patient_name', 'label': 'Patient Name', 'fieldtype': 'Data', 'fetch_from': 'patient.patient_name',
- 'insert_after': 'patient', 'read_only': True
- },
- {
- 'fieldname': 'ref_practitioner', 'label': 'Referring Practitioner', 'fieldtype': 'Link', 'options': 'Healthcare Practitioner',
- 'insert_after': 'customer'
- }
- ],
- 'Sales Invoice Item': [
- {
- 'fieldname': 'reference_dt', 'label': 'Reference DocType', 'fieldtype': 'Link', 'options': 'DocType',
- 'insert_after': 'edit_references'
- },
- {
- 'fieldname': 'reference_dn', 'label': 'Reference Name', 'fieldtype': 'Dynamic Link', 'options': 'reference_dt',
- 'insert_after': 'reference_dt'
- }
- ],
- 'Stock Entry': [
- {
- 'fieldname': 'inpatient_medication_entry', 'label': 'Inpatient Medication Entry', 'fieldtype': 'Link', 'options': 'Inpatient Medication Entry',
- 'insert_after': 'credit_note', 'read_only': True
- }
- ],
- 'Stock Entry Detail': [
- {
- 'fieldname': 'patient', 'label': 'Patient', 'fieldtype': 'Link', 'options': 'Patient',
- 'insert_after': 'po_detail', 'read_only': True
- },
- {
- 'fieldname': 'inpatient_medication_entry_child', 'label': 'Inpatient Medication Entry Child', 'fieldtype': 'Data',
- 'insert_after': 'patient', 'read_only': True
- }
- ]
- },
- 'on_setup': 'erpnext.healthcare.setup.setup_healthcare'
-}
diff --git a/erpnext/domains/manufacturing.py b/erpnext/domains/manufacturing.py
index 259ee92..b9ad49e 100644
--- a/erpnext/domains/manufacturing.py
+++ b/erpnext/domains/manufacturing.py
@@ -21,4 +21,4 @@
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
-}
\ No newline at end of file
+}
diff --git a/erpnext/domains/non_profit.py b/erpnext/domains/non_profit.py
index b6772c5..7c4f6b1 100644
--- a/erpnext/domains/non_profit.py
+++ b/erpnext/domains/non_profit.py
@@ -21,4 +21,4 @@
'Non Profit'
],
'default_portal_role': 'Non Profit Manager'
-}
\ No newline at end of file
+}
diff --git a/erpnext/domains/services.py b/erpnext/domains/services.py
index 7a4ffc4..8921372 100644
--- a/erpnext/domains/services.py
+++ b/erpnext/domains/services.py
@@ -18,4 +18,4 @@
['Stock Settings', None, 'show_barcode_field', 0]
],
'default_portal_role': 'Customer'
-}
\ No newline at end of file
+}
diff --git a/erpnext/education/__init__.py b/erpnext/education/__init__.py
index c0589bb..a3164b2 100644
--- a/erpnext/education/__init__.py
+++ b/erpnext/education/__init__.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
class StudentNotInGroupError(frappe.ValidationError): pass
def validate_student_belongs_to_group(student, student_group):
diff --git a/erpnext/education/api.py b/erpnext/education/api.py
index 4493a3f..d2a8805 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
from frappe import _
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils import flt, cstr, getdate
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
diff --git a/erpnext/education/doctype/academic_term/academic_term.py b/erpnext/education/doctype/academic_term/academic_term.py
index 3aa0be15..b8e22b6 100644
--- a/erpnext/education/doctype/academic_term/academic_term.py
+++ b/erpnext/education/doctype/academic_term/academic_term.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate
from frappe.model.document import Document
+from frappe.utils import getdate
+
class AcademicTerm(Document):
def autoname(self):
@@ -22,9 +24,9 @@
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
+ # 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))
diff --git a/erpnext/education/doctype/academic_term/academic_term_dashboard.py b/erpnext/education/doctype/academic_term/academic_term_dashboard.py
index 871e0f3..a1087b8 100644
--- a/erpnext/education/doctype/academic_term/academic_term_dashboard.py
+++ b/erpnext/education/doctype/academic_term/academic_term_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'academic_term',
@@ -22,4 +24,4 @@
'items': ['Assessment Plan', 'Assessment Result']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/academic_term/test_academic_term.js b/erpnext/education/doctype/academic_term/test_academic_term.js
index 6d91e97..383b65a 100644
--- a/erpnext/education/doctype/academic_term/test_academic_term.js
+++ b/erpnext/education/doctype/academic_term/test_academic_term.js
@@ -21,4 +21,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/academic_term/test_academic_term.py b/erpnext/education/doctype/academic_term/test_academic_term.py
index 0964a56..6329103 100644
--- a/erpnext/education/doctype/academic_term/test_academic_term.py
+++ b/erpnext/education/doctype/academic_term/test_academic_term.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Academic Term')
diff --git a/erpnext/education/doctype/academic_year/academic_year.js b/erpnext/education/doctype/academic_year/academic_year.js
index 0e86198..20e2528 100644
--- a/erpnext/education/doctype/academic_year/academic_year.js
+++ b/erpnext/education/doctype/academic_year/academic_year.js
@@ -1,2 +1,2 @@
frappe.ui.form.on("Academic Year", {
-});
\ 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
index f2858a4..77b67d8 100644
--- a/erpnext/education/doctype/academic_year/academic_year.py
+++ b/erpnext/education/doctype/academic_year/academic_year.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
+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
diff --git a/erpnext/education/doctype/academic_year/academic_year_dashboard.py b/erpnext/education/doctype/academic_year/academic_year_dashboard.py
index f27f7d1..49d68c3 100644
--- a/erpnext/education/doctype/academic_year/academic_year_dashboard.py
+++ b/erpnext/education/doctype/academic_year/academic_year_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'academic_year',
@@ -22,4 +24,4 @@
'items': ['Assessment Plan', 'Assessment Result']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/academic_year/test_academic_year.js b/erpnext/education/doctype/academic_year/test_academic_year.js
deleted file mode 100644
index ec2f49c..0000000
--- a/erpnext/education/doctype/academic_year/test_academic_year.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Testing Setup Module in Education
-QUnit.module('education');
-
-QUnit.test('Test: Academic Year', function(assert){
- assert.expect(3);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make('Academic Year', [
- {academic_year_name: '2016-17'},
- {year_start_date: '2016-07-20'},
- {year_end_date:'2017-06-20'},
- ]);
- },
-
- () => {
- assert.ok(cur_frm.doc.academic_year_name=='2016-17');
- assert.ok(cur_frm.doc.year_start_date=='2016-07-20');
- assert.ok(cur_frm.doc.year_end_date=='2017-06-20');
- },
- () => done()
- ]);
-});
\ No newline at end of file
diff --git a/erpnext/education/doctype/academic_year/test_academic_year.py b/erpnext/education/doctype/academic_year/test_academic_year.py
index 9da75a7..31135c4 100644
--- a/erpnext/education/doctype/academic_year/test_academic_year.py
+++ b/erpnext/education/doctype/academic_year/test_academic_year.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Academic Year')
diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js
index edfec26..85b387f 100644
--- a/erpnext/education/doctype/article/article.js
+++ b/erpnext/education/doctype/article/article.js
@@ -53,4 +53,4 @@
method: 'erpnext.education.doctype.article.article.get_topics_without_article',
args: {'article': article}
});
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/article/article.py b/erpnext/education/doctype/article/article.py
index 8ba367d..f3c7788 100644
--- a/erpnext/education/doctype/article/article.py
+++ b/erpnext/education/doctype/article/article.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class Article(Document):
def get_article(self):
pass
@@ -18,4 +20,4 @@
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
\ No newline at end of file
+ return data
diff --git a/erpnext/education/doctype/article/test_article.js b/erpnext/education/doctype/article/test_article.js
deleted file mode 100644
index 9dbf063..0000000
--- a/erpnext/education/doctype/article/test_article.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Article", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Article
- () => frappe.tests.make('Article', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/article/test_article.py b/erpnext/education/doctype/article/test_article.py
index 2fce07f..cda79ad 100644
--- a/erpnext/education/doctype/article/test_article.py
+++ b/erpnext/education/doctype/article/test_article.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestArticle(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
index 1ea3702..f8f04bf 100644
--- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
+++ b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
@@ -12,4 +13,4 @@
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"))
\ No newline at end of file
+ frappe.throw(_("Can't create standard criteria. Please rename the criteria"))
diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js
index db4a4cf..724c4da 100644
--- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js
+++ b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js
@@ -13,4 +13,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
index fc0d745..1098d03 100644
--- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
+++ b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Assessment Criteria')
diff --git a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py b/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
index 75381e1..e62c030 100644
--- a/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
+++ b/erpnext/education/doctype/assessment_criteria_group/assessment_criteria_group.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssessmentCriteriaGroup(Document):
pass
diff --git a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js
index bcfcaf8..ab27e63 100644
--- a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js
+++ b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js
@@ -12,4 +12,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
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
index 5b29337..d65f1e7 100644
--- a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py
+++ b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Assessment Criteria Group')
diff --git a/erpnext/education/doctype/assessment_group/assessment_group.py b/erpnext/education/doctype/assessment_group/assessment_group.py
index 88acc12..3425109 100644
--- a/erpnext/education/doctype/assessment_group/assessment_group.py
+++ b/erpnext/education/doctype/assessment_group/assessment_group.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
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
index 2649d4b..83438c0 100644
--- a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py
+++ b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'assessment_group',
@@ -12,4 +14,4 @@
'items': ['Assessment Plan', 'Assessment Result']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/assessment_group/assessment_group_tree.js b/erpnext/education/doctype/assessment_group/assessment_group_tree.js
index e467683..e0dfaa3 100644
--- a/erpnext/education/doctype/assessment_group/assessment_group_tree.js
+++ b/erpnext/education/doctype/assessment_group/assessment_group_tree.js
@@ -1,3 +1,3 @@
frappe.treeview_settings["Assessment Group"] = {
-
-}
\ No newline at end of file
+
+}
diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.js b/erpnext/education/doctype/assessment_group/test_assessment_group.js
index a127fd4..00e6309 100644
--- a/erpnext/education/doctype/assessment_group/test_assessment_group.js
+++ b/erpnext/education/doctype/assessment_group/test_assessment_group.js
@@ -62,4 +62,4 @@
() => frappe.click_button('Create New'),
]);
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.py b/erpnext/education/doctype/assessment_group/test_assessment_group.py
index 2fd98b6..822d65c 100644
--- a/erpnext/education/doctype/assessment_group/test_assessment_group.py
+++ b/erpnext/education/doctype/assessment_group/test_assessment_group.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Assessment Group')
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.js b/erpnext/education/doctype/assessment_plan/assessment_plan.js
index 726c0fc..cf545c4 100644
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.js
+++ b/erpnext/education/doctype/assessment_plan/assessment_plan.js
@@ -75,4 +75,4 @@
maximum_assessment_score: function(frm) {
frm.trigger('course');
}
-});
\ 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
index 16136c1..2a58a31 100644
--- a/erpnext/education/doctype/assessment_plan/assessment_plan.py
+++ b/erpnext/education/doctype/assessment_plan/assessment_plan.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
+
import frappe
from frappe import _
+from frappe.model.document import Document
+
class AssessmentPlan(Document):
def validate(self):
diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
index 5e6c29d..6729538 100644
--- a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
+++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'assessment_plan',
@@ -18,4 +20,4 @@
'items': ['Assessment Plan Status']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/assessment_plan/test_assessment_plan.py b/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
index 2de4f23..9a6b886 100644
--- a/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
+++ b/erpnext/education/doctype/assessment_plan/test_assessment_plan.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Assessment Plan')
diff --git a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py b/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
index 53b477f..795462d 100644
--- a/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
+++ b/erpnext/education/doctype/assessment_plan_criteria/assessment_plan_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssessmentPlanCriteria(Document):
pass
diff --git a/erpnext/education/doctype/assessment_result/assessment_result.js b/erpnext/education/doctype/assessment_result/assessment_result.js
index c35f607..b6d2881 100644
--- a/erpnext/education/doctype/assessment_result/assessment_result.js
+++ b/erpnext/education/doctype/assessment_result/assessment_result.js
@@ -122,4 +122,4 @@
});
}
}
-});
\ 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
index 6b873ec..01f483f 100644
--- a/erpnext/education/doctype/assessment_result/assessment_result.py
+++ b/erpnext/education/doctype/assessment_result/assessment_result.py
@@ -3,14 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
from frappe.model.document import Document
-from erpnext.education.api import get_grade
-from erpnext.education.api import get_assessment_details
+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):
@@ -42,7 +44,3 @@
"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
index 438379d..f9e2008 100644
--- a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
+++ b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'reports': [
@@ -11,4 +13,4 @@
'items': ['Final Assessment Grades', 'Course wise Assessment Report']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.js b/erpnext/education/doctype/assessment_result/test_assessment_result.js
index b7adfac..d4eb4b8 100644
--- a/erpnext/education/doctype/assessment_result/test_assessment_result.js
+++ b/erpnext/education/doctype/assessment_result/test_assessment_result.js
@@ -70,4 +70,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.py b/erpnext/education/doctype/assessment_result/test_assessment_result.py
index e5535d6..fa0ad1f 100644
--- a/erpnext/education/doctype/assessment_result/test_assessment_result.py
+++ b/erpnext/education/doctype/assessment_result/test_assessment_result.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
from erpnext.education.api import get_grade
# test_records = frappe.get_test_records('Assessment Result')
@@ -16,4 +16,3 @@
grade = get_grade("_Test Grading Scale", 70)
self.assertEqual("B", grade)
-
\ 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
index d051593..234dff0 100644
--- a/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py
+++ b/erpnext/education/doctype/assessment_result_detail/assessment_result_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssessmentResultDetail(Document):
pass
diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
index 649f420..83b4f56 100644
--- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
+++ b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class AssessmentResultTool(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js
index 0bbe331..7ef5c68 100644
--- a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js
+++ b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js
@@ -26,4 +26,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
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
index f784ccb..bcc5722 100644
--- a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py
+++ b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestAssessmentResultTool(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/content_activity/content_activity.py b/erpnext/education/doctype/content_activity/content_activity.py
index 2ae7a5c..076e2d3 100644
--- a/erpnext/education/doctype/content_activity/content_activity.py
+++ b/erpnext/education/doctype/content_activity/content_activity.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ContentActivity(Document):
pass
diff --git a/erpnext/education/doctype/content_question/content_question.py b/erpnext/education/doctype/content_question/content_question.py
index b239d21..9c24916 100644
--- a/erpnext/education/doctype/content_question/content_question.py
+++ b/erpnext/education/doctype/content_question/content_question.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ContentQuestion(Document):
pass
diff --git a/erpnext/education/doctype/content_question/test_content_question.js b/erpnext/education/doctype/content_question/test_content_question.js
deleted file mode 100644
index cc869a8..0000000
--- a/erpnext/education/doctype/content_question/test_content_question.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Content Question", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Content Question
- () => frappe.tests.make('Content Question', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/content_question/test_content_question.py b/erpnext/education/doctype/content_question/test_content_question.py
index 268b9be..f6bd49b 100644
--- a/erpnext/education/doctype/content_question/test_content_question.py
+++ b/erpnext/education/doctype/content_question/test_content_question.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestContentQuestion(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/course/course.js b/erpnext/education/doctype/course/course.js
index 81e4a8c..bd8d62c 100644
--- a/erpnext/education/doctype/course/course.js
+++ b/erpnext/education/doctype/course/course.js
@@ -76,4 +76,4 @@
method: 'erpnext.education.doctype.course.course.get_programs_without_course',
args: {'course': course}
});
-}
\ No newline at end of file
+}
diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py
index 06efa54..9cc373a 100644
--- a/erpnext/education/doctype/course/course.py
+++ b/erpnext/education/doctype/course/course.py
@@ -3,10 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from frappe.model.document import Document
+
+import frappe
from frappe import _
+from frappe.model.document import Document
+
class Course(Document):
def validate(self):
@@ -53,4 +56,4 @@
courses = [c.course for c in program.courses]
if not courses or course not in courses:
data.append(program.name)
- return data
\ No newline at end of file
+ return data
diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py
index 8a570bd..8eca2a1 100644
--- a/erpnext/education/doctype/course/course_dashboard.py
+++ b/erpnext/education/doctype/course/course_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'course',
@@ -20,4 +22,4 @@
'items': ['Assessment Plan', 'Assessment Result']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/course/test_course.js b/erpnext/education/doctype/course/test_course.js
index 88fddc2..2b6860c 100644
--- a/erpnext/education/doctype/course/test_course.js
+++ b/erpnext/education/doctype/course/test_course.js
@@ -33,4 +33,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/course/test_course.py b/erpnext/education/doctype/course/test_course.py
index 4667ac4..dd43ef4 100644
--- a/erpnext/education/doctype/course/test_course.py
+++ b/erpnext/education/doctype/course/test_course.py
@@ -2,11 +2,12 @@
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
-from erpnext.education.doctype.topic.test_topic import make_topic
-from erpnext.education.doctype.topic.test_topic import make_topic_and_linked_content
+
+import unittest
import frappe
-import unittest
+
+from erpnext.education.doctype.topic.test_topic import make_topic, make_topic_and_linked_content
# test_records = frappe.get_test_records('Course')
diff --git a/erpnext/education/doctype/course_activity/course_activity.py b/erpnext/education/doctype/course_activity/course_activity.py
index e7fc08a..61b51a0 100644
--- a/erpnext/education/doctype/course_activity/course_activity.py
+++ b/erpnext/education/doctype/course_activity/course_activity.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class CourseActivity(Document):
def validate(self):
self.check_if_enrolled()
@@ -16,4 +18,4 @@
if frappe.db.exists("Course Enrollment", self.enrollment):
return True
else:
- frappe.throw(_("Course Enrollment {0} does not exists").format(self.enrollment))
\ No newline at end of file
+ frappe.throw(_("Course Enrollment {0} does not exists").format(self.enrollment))
diff --git a/erpnext/education/doctype/course_activity/test_course_activity.js b/erpnext/education/doctype/course_activity/test_course_activity.js
deleted file mode 100644
index c89c89e..0000000
--- a/erpnext/education/doctype/course_activity/test_course_activity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Activity", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Course Activity
- () => frappe.tests.make('Course Activity', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/course_activity/test_course_activity.py b/erpnext/education/doctype/course_activity/test_course_activity.py
index 5269a6b..778cefe 100644
--- a/erpnext/education/doctype/course_activity/test_course_activity.py
+++ b/erpnext/education/doctype/course_activity/test_course_activity.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestCourseActivity(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py b/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
index ade2a39..df384c5 100644
--- a/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
+++ b/erpnext/education/doctype/course_assessment_criteria/course_assessment_criteria.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CourseAssessmentCriteria(Document):
pass
diff --git a/erpnext/education/doctype/course_content/course_content.py b/erpnext/education/doctype/course_content/course_content.py
index 0d2f85a..1dd08ad 100644
--- a/erpnext/education/doctype/course_content/course_content.py
+++ b/erpnext/education/doctype/course_content/course_content.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CourseContent(Document):
pass
diff --git a/erpnext/education/doctype/course_content/test_course_content.js b/erpnext/education/doctype/course_content/test_course_content.js
deleted file mode 100644
index 786e67e..0000000
--- a/erpnext/education/doctype/course_content/test_course_content.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Content", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Course Content
- () => frappe.tests.make('Course Content', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/course_content/test_course_content.py b/erpnext/education/doctype/course_content/test_course_content.py
index 9be4b1f..320fa11 100644
--- a/erpnext/education/doctype/course_content/test_course_content.py
+++ b/erpnext/education/doctype/course_content/test_course_content.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCourseContent(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.py b/erpnext/education/doctype/course_enrollment/course_enrollment.py
index ce88990..21e7451 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from functools import reduce
+
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_link_to_form
-from functools import reduce
+
class CourseEnrollment(Document):
def validate(self):
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
index b9dd457..253325c 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'enrollment',
@@ -12,4 +14,4 @@
'items': ['Course Activity', 'Quiz Activity']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.js b/erpnext/education/doctype/course_enrollment/test_course_enrollment.js
deleted file mode 100644
index 216cc30..0000000
--- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Enrollment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Course Enrollment
- () => frappe.tests.make('Course Enrollment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
index e22c7ce..e5feb1b 100644
--- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py
@@ -3,14 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
-from erpnext.education.doctype.student.test_student import create_student
-from erpnext.education.doctype.student.test_student import get_student
-from erpnext.education.doctype.program.test_program import setup_program
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):
@@ -39,6 +39,3 @@
doc = frappe.get_doc("Program Enrollment", entry.name)
doc.cancel()
doc.delete()
-
-
-
diff --git a/erpnext/education/doctype/course_schedule/course_schedule.js b/erpnext/education/doctype/course_schedule/course_schedule.js
index 4275f6e..366bbd8 100644
--- a/erpnext/education/doctype/course_schedule/course_schedule.js
+++ b/erpnext/education/doctype/course_schedule/course_schedule.js
@@ -13,4 +13,4 @@
}).addClass("btn-primary");
}
}
-});
\ 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
index 5083ff6..38379e4 100644
--- a/erpnext/education/doctype/course_schedule/course_schedule.py
+++ b/erpnext/education/doctype/course_schedule/course_schedule.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
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")
@@ -14,11 +16,11 @@
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":
@@ -28,23 +30,22 @@
"""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."))
-
+
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)
-
diff --git a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
index 0866cd6..12a1735f 100644
--- a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
+++ b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'course_schedule',
@@ -12,4 +14,4 @@
'items': ['Student Attendance']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/course_schedule/test_course_schedule.js b/erpnext/education/doctype/course_schedule/test_course_schedule.js
deleted file mode 100644
index 5cdb67b..0000000
--- a/erpnext/education/doctype/course_schedule/test_course_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Schedule", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Course Schedule
- () => frappe.tests.make('Course Schedule', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/course_schedule/test_course_schedule.py b/erpnext/education/doctype/course_schedule/test_course_schedule.py
index a901f1e..1b45ced 100644
--- a/erpnext/education/doctype/course_schedule/test_course_schedule.py
+++ b/erpnext/education/doctype/course_schedule/test_course_schedule.py
@@ -3,13 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
+import datetime
import unittest
-import datetime
-from frappe.utils import today, to_timedelta
+import frappe
+from frappe.utils import to_timedelta, today
+
from erpnext.education.utils import OverlapError
-from frappe.utils.make_random import get_random
# test_records = frappe.get_test_records('Course Schedule')
@@ -17,28 +17,28 @@
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,
+ 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,
+
+ 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,
+
+ 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,
+
+ 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 make_course_schedule_test_record(**args):
@@ -49,12 +49,12 @@
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:
diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js
index d57f46a..7b0e4ab 100644
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js
+++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js
@@ -41,4 +41,4 @@
});
});
}
-});
\ 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
index 0f2ea96..4f7ed36 100644
--- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
+++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
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
@@ -95,7 +98,7 @@
if self.day == calendar.day_name[getdate(d.schedule_date).weekday()]:
frappe.delete_doc("Course Schedule", d.name)
rescheduled.append(d.name)
- except:
+ except Exception:
reschedule_errors.append(d.name)
return rescheduled, reschedule_errors
diff --git a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.js
deleted file mode 100644
index 4419d18..0000000
--- a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Scheduling Tool", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Course Scheduling Tool
- () => frappe.tests.make('Course Scheduling Tool', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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
index d921f8e..27379b7 100644
--- a/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.py
+++ b/erpnext/education/doctype/course_scheduling_tool/test_course_scheduling_tool.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCourseSchedulingTool(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/course_topic/course_topic.py b/erpnext/education/doctype/course_topic/course_topic.py
index 2364f17..11eb457 100644
--- a/erpnext/education/doctype/course_topic/course_topic.py
+++ b/erpnext/education/doctype/course_topic/course_topic.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CourseTopic(Document):
pass
diff --git a/erpnext/education/doctype/course_topic/test_course_topic.js b/erpnext/education/doctype/course_topic/test_course_topic.js
deleted file mode 100644
index d8d154f..0000000
--- a/erpnext/education/doctype/course_topic/test_course_topic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Course Topic", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Course Topic
- () => frappe.tests.make('Course Topic', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/course_topic/test_course_topic.py b/erpnext/education/doctype/course_topic/test_course_topic.py
index 7ce46d2..0bba7f5 100644
--- a/erpnext/education/doctype/course_topic/test_course_topic.py
+++ b/erpnext/education/doctype/course_topic/test_course_topic.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCourseTopic(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/education_settings/education_settings.py b/erpnext/education/doctype/education_settings/education_settings.py
index 658380e..71d13f7 100644
--- a/erpnext/education/doctype/education_settings/education_settings.py
+++ b/erpnext/education/doctype/education_settings/education_settings.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
import frappe.defaults
from frappe.model.document import Document
@@ -36,4 +37,4 @@
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
\ No newline at end of file
+ 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
index 038fb6e..3611cbe 100644
--- a/erpnext/education/doctype/education_settings/test_education_settings.py
+++ b/erpnext/education/doctype/education_settings/test_education_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEducationSettings(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/fee_category/fee_category.py b/erpnext/education/doctype/fee_category/fee_category.py
index 5523444..f531f8a 100644
--- a/erpnext/education/doctype/fee_category/fee_category.py
+++ b/erpnext/education/doctype/fee_category/fee_category.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class FeeCategory(Document):
pass
diff --git a/erpnext/education/doctype/fee_category/test_fee_category.js b/erpnext/education/doctype/fee_category/test_fee_category.js
deleted file mode 100644
index a08ed33..0000000
--- a/erpnext/education/doctype/fee_category/test_fee_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Fee Category
- () => frappe.tests.make('Fee Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/fee_category/test_fee_category.py b/erpnext/education/doctype/fee_category/test_fee_category.py
index 48e7589..8755684 100644
--- a/erpnext/education/doctype/fee_category/test_fee_category.py
+++ b/erpnext/education/doctype/fee_category/test_fee_category.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Fee Category')
diff --git a/erpnext/education/doctype/fee_component/fee_component.py b/erpnext/education/doctype/fee_component/fee_component.py
index 8694610..dba39af 100644
--- a/erpnext/education/doctype/fee_component/fee_component.py
+++ b/erpnext/education/doctype/fee_component/fee_component.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class FeeComponent(Document):
pass
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js
index 0089957..97691a5 100644
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.js
+++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js
@@ -130,4 +130,4 @@
});
}
}
-})
\ 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
index 0b025c7..6bf4667 100644
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.py
+++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py
@@ -3,13 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
+from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
-from frappe.utils import money_in_words
-from frappe.utils import cint, flt, cstr
+from frappe.utils import cint, cstr, flt, money_in_words
from frappe.utils.background_jobs import enqueue
-from frappe import _
+
+import erpnext
class FeeSchedule(Document):
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
index acfe400..34f8705 100644
--- a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
+++ b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'fee_schedule',
@@ -10,4 +11,4 @@
'items': ['Fees']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/fee_schedule/test_fee_schedule.js b/erpnext/education/doctype/fee_schedule/test_fee_schedule.js
deleted file mode 100644
index d495b4c..0000000
--- a/erpnext/education/doctype/fee_schedule/test_fee_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Schedule", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Fee Schedule', [
- // insert a new Fee Schedule
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/fee_schedule/test_fee_schedule.py b/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
index 44e0756..86b74de 100644
--- a/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
+++ b/erpnext/education/doctype/fee_schedule/test_fee_schedule.py
@@ -5,5 +5,6 @@
import unittest
+
class TestFeeSchedule(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py b/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
index 11d5697..5e9ed61 100644
--- a/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
+++ b/erpnext/education/doctype/fee_schedule_program/fee_schedule_program.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class FeeScheduleProgram(Document):
pass
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
index 776534d..ba30a91 100644
--- 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
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class FeeScheduleStudentGroup(Document):
pass
diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js
index 310c410..d9ab99f 100644
--- a/erpnext/education/doctype/fee_structure/fee_structure.js
+++ b/erpnext/education/doctype/fee_structure/fee_structure.js
@@ -69,4 +69,4 @@
}
frm.set_value('total_amount', total_amount);
}
-});
\ 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
index 781382b..a6cc701 100644
--- a/erpnext/education/doctype/fee_structure/fee_structure.py
+++ b/erpnext/education/doctype/fee_structure/fee_structure.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
@@ -11,13 +12,13 @@
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):
@@ -31,4 +32,4 @@
"Fee Component": {
"doctype": "Fee Component"
}
- }, target_doc)
\ No newline at end of file
+ }, target_doc)
diff --git a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
index 73e314f..c053b4e 100644
--- a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
+++ b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'fee_structure',
@@ -12,4 +14,4 @@
'items': ['Fees', 'Fee Schedule']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/fee_structure/test_fee_structure.js b/erpnext/education/doctype/fee_structure/test_fee_structure.js
deleted file mode 100644
index 61f4135..0000000
--- a/erpnext/education/doctype/fee_structure/test_fee_structure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Structure", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Fee Structure
- () => frappe.tests.make('Fee Structure', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/fee_structure/test_fee_structure.py b/erpnext/education/doctype/fee_structure/test_fee_structure.py
index 785ae4e..1311f13 100644
--- a/erpnext/education/doctype/fee_structure/test_fee_structure.py
+++ b/erpnext/education/doctype/fee_structure/test_fee_structure.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Fee Structure')
diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py
index 25d67d2..a5dc0dc 100644
--- a/erpnext/education/doctype/fees/fees.py
+++ b/erpnext/education/doctype/fees/fees.py
@@ -3,14 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
-import frappe, erpnext
+
+import frappe
from frappe import _
from frappe.utils import money_in_words
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from frappe.utils.csvutils import getlink
-from erpnext.controllers.accounts_controller import AccountsController
+
+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):
@@ -132,4 +134,4 @@
"title": _("Fees"),
"get_list": get_fee_list,
"row_template": "templates/includes/fee/fee_row.html"
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/fees/fees_list.js b/erpnext/education/doctype/fees/fees_list.js
index 52e1c4b..ee8e1e3 100644
--- a/erpnext/education/doctype/fees/fees_list.js
+++ b/erpnext/education/doctype/fees/fees_list.js
@@ -9,4 +9,4 @@
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/fees/test_fees.py b/erpnext/education/doctype/fees/test_fees.py
index c6bb704..fbf7a57 100644
--- a/erpnext/education/doctype/fees/test_fees.py
+++ b/erpnext/education/doctype/fees/test_fees.py
@@ -3,10 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
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']
diff --git a/erpnext/education/doctype/grading_scale/grading_scale.py b/erpnext/education/doctype/grading_scale/grading_scale.py
index 6309d02..ed75f31 100644
--- a/erpnext/education/doctype/grading_scale/grading_scale.py
+++ b/erpnext/education/doctype/grading_scale/grading_scale.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cint
from frappe.model.document import Document
+from frappe.utils import cint
+
class GradingScale(Document):
def validate(self):
@@ -17,4 +19,4 @@
else:
thresholds.append(cint(d.threshold))
if 0 not in thresholds:
- frappe.throw(_("Please define grade for Threshold 0%"))
\ No newline at end of file
+ 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
index 2a3f13b..268871a 100644
--- a/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
+++ b/erpnext/education/doctype/grading_scale/grading_scale_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'grading_scale',
diff --git a/erpnext/education/doctype/grading_scale/test_grading_scale.js b/erpnext/education/doctype/grading_scale/test_grading_scale.js
index e363545..fb56918 100644
--- a/erpnext/education/doctype/grading_scale/test_grading_scale.js
+++ b/erpnext/education/doctype/grading_scale/test_grading_scale.js
@@ -99,4 +99,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/grading_scale/test_grading_scale.py b/erpnext/education/doctype/grading_scale/test_grading_scale.py
index 5364d7c..e5d83c2 100644
--- a/erpnext/education/doctype/grading_scale/test_grading_scale.py
+++ b/erpnext/education/doctype/grading_scale/test_grading_scale.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Grading Scale')
diff --git a/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py b/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
index 41ac5ff..6e55aac 100644
--- a/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
+++ b/erpnext/education/doctype/grading_scale_interval/grading_scale_interval.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class GradingScaleInterval(Document):
pass
diff --git a/erpnext/education/doctype/guardian/guardian.py b/erpnext/education/doctype/guardian/guardian.py
index e82cc54..f79e131 100644
--- a/erpnext/education/doctype/guardian/guardian.py
+++ b/erpnext/education/doctype/guardian/guardian.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
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()
diff --git a/erpnext/education/doctype/guardian/test_guardian.js b/erpnext/education/doctype/guardian/test_guardian.js
index 9bbfacd..1ea6dc2 100644
--- a/erpnext/education/doctype/guardian/test_guardian.js
+++ b/erpnext/education/doctype/guardian/test_guardian.js
@@ -31,4 +31,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/guardian/test_guardian.py b/erpnext/education/doctype/guardian/test_guardian.py
index 61420f6..446e261 100644
--- a/erpnext/education/doctype/guardian/test_guardian.py
+++ b/erpnext/education/doctype/guardian/test_guardian.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Guardian')
diff --git a/erpnext/education/doctype/guardian_interest/guardian_interest.py b/erpnext/education/doctype/guardian_interest/guardian_interest.py
index f5c4cf1..4a3040f 100644
--- a/erpnext/education/doctype/guardian_interest/guardian_interest.py
+++ b/erpnext/education/doctype/guardian_interest/guardian_interest.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class GuardianInterest(Document):
pass
diff --git a/erpnext/education/doctype/guardian_student/guardian_student.py b/erpnext/education/doctype/guardian_student/guardian_student.py
index bf6f5c1..62867d8 100644
--- a/erpnext/education/doctype/guardian_student/guardian_student.py
+++ b/erpnext/education/doctype/guardian_student/guardian_student.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class GuardianStudent(Document):
pass
diff --git a/erpnext/education/doctype/instructor/instructor.js b/erpnext/education/doctype/instructor/instructor.js
index 24e80fa..034b0aa 100644
--- a/erpnext/education/doctype/instructor/instructor.js
+++ b/erpnext/education/doctype/instructor/instructor.js
@@ -61,4 +61,4 @@
};
});
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/instructor/instructor.py b/erpnext/education/doctype/instructor/instructor.py
index b1bfcbb..92fb8b0 100644
--- a/erpnext/education/doctype/instructor/instructor.py
+++ b/erpnext/education/doctype/instructor/instructor.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
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")
diff --git a/erpnext/education/doctype/instructor/instructor_dashboard.py b/erpnext/education/doctype/instructor/instructor_dashboard.py
index a404fc5..bb08a54 100644
--- a/erpnext/education/doctype/instructor/instructor_dashboard.py
+++ b/erpnext/education/doctype/instructor/instructor_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
@@ -21,4 +23,4 @@
'items': ['Student Group']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/instructor/test_instructor.py b/erpnext/education/doctype/instructor/test_instructor.py
index 4061422..b698a20 100644
--- a/erpnext/education/doctype/instructor/test_instructor.py
+++ b/erpnext/education/doctype/instructor/test_instructor.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Instructor')
diff --git a/erpnext/education/doctype/instructor_log/instructor_log.py b/erpnext/education/doctype/instructor_log/instructor_log.py
index 75217b2..68ab7a6 100644
--- a/erpnext/education/doctype/instructor_log/instructor_log.py
+++ b/erpnext/education/doctype/instructor_log/instructor_log.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class InstructorLog(Document):
pass
diff --git a/erpnext/education/doctype/options/options.py b/erpnext/education/doctype/options/options.py
index a11d77a..a064384 100644
--- a/erpnext/education/doctype/options/options.py
+++ b/erpnext/education/doctype/options/options.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class Options(Document):
pass
diff --git a/erpnext/education/doctype/program/program.js b/erpnext/education/doctype/program/program.js
index 98263b5..2d89351 100644
--- a/erpnext/education/doctype/program/program.js
+++ b/erpnext/education/doctype/program/program.js
@@ -4,5 +4,5 @@
cur_frm.add_fetch('fee_structure', 'total_amount', 'amount');
frappe.ui.form.on("Program", "refresh", function(frm) {
-
-});
\ No newline at end of file
+
+});
diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py
index d24df5d..7de34cf 100644
--- a/erpnext/education/doctype/program/program.py
+++ b/erpnext/education/doctype/program/program.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
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
\ No newline at end of file
+ return course_list
diff --git a/erpnext/education/doctype/program/program_dashboard.py b/erpnext/education/doctype/program/program_dashboard.py
index c5d2494..6696076 100644
--- a/erpnext/education/doctype/program/program_dashboard.py
+++ b/erpnext/education/doctype/program/program_dashboard.py
@@ -1,5 +1,6 @@
from frappe import _
+
def get_data():
return {
'fieldname': 'program',
@@ -21,4 +22,4 @@
'items': ['Assessment Plan', 'Assessment Result']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/program/test_program.js b/erpnext/education/doctype/program/test_program.js
index dc347cf..b9ca41a 100644
--- a/erpnext/education/doctype/program/test_program.js
+++ b/erpnext/education/doctype/program/test_program.js
@@ -31,4 +31,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py
index d753036..3222aa6 100644
--- a/erpnext/education/doctype/program/test_program.py
+++ b/erpnext/education/doctype/program/test_program.py
@@ -2,12 +2,13 @@
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
-from erpnext.education.doctype.course.test_course import make_course
-from erpnext.education.doctype.topic.test_topic import make_topic_and_linked_content
-from erpnext.education.doctype.course.test_course import make_course_and_linked_topic
+
+import unittest
import frappe
-import unittest
+
+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",
@@ -88,4 +89,4 @@
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
\ No newline at end of file
+ return program
diff --git a/erpnext/education/doctype/program_course/program_course.py b/erpnext/education/doctype/program_course/program_course.py
index 684b6fa..d5236a1 100644
--- a/erpnext/education/doctype/program_course/program_course.py
+++ b/erpnext/education/doctype/program_course/program_course.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProgramCourse(Document):
pass
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.js b/erpnext/education/doctype/program_enrollment/program_enrollment.js
index f9c65fb..e92d063 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.js
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment.js
@@ -101,4 +101,4 @@
return { filters: [['Course', 'name', 'not in', course_list]] };
};
}
-});
\ 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
index b282bab..79c5a14 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py
@@ -3,12 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
+from frappe import _, msgprint
+from frappe.desk.reportview import get_match_cond
from frappe.model.document import Document
-from frappe.desk.reportview import get_match_cond, get_filters_cond
from frappe.utils import comma_and, get_link_to_form, getdate
-import erpnext.www.lms as lms
+
class ProgramEnrollment(Document):
def validate(self):
@@ -174,4 +175,3 @@
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
index 18d307c..f829276 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'program_enrollment',
@@ -16,4 +18,4 @@
'items': ['Student and Guardian Contact Details']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.js b/erpnext/education/doctype/program_enrollment/test_program_enrollment.js
deleted file mode 100644
index aea81a0..0000000
--- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Program Enrollment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Program Enrollment
- () => frappe.tests.make('Program Enrollment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
index fec6422..65de38a 100644
--- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py
@@ -3,13 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.education.doctype.student.test_student import create_student
-from erpnext.education.doctype.student.test_student import get_student
+import frappe
+
from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
-from erpnext.education.doctype.course_activity.test_course_activity import make_course_activity
+from erpnext.education.doctype.student.test_student import create_student, get_student
+
class TestProgramEnrollment(unittest.TestCase):
@@ -32,4 +32,4 @@
for entry in frappe.db.get_all("Program Enrollment"):
doc = frappe.get_doc("Program Enrollment", entry.name)
doc.cancel()
- doc.delete()
\ No newline at end of file
+ doc.delete()
diff --git a/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py b/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
index e1f564e..5c0706d 100644
--- a/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
+++ b/erpnext/education/doctype/program_enrollment_course/program_enrollment_course.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProgramEnrollmentCourse(Document):
pass
diff --git a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py b/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
index 03a311e..53bae7c 100644
--- a/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
+++ b/erpnext/education/doctype/program_enrollment_fee/program_enrollment_fee.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProgramEnrollmentFee(Document):
pass
diff --git a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
index 5833b67..69fa665 100644
--- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
+++ b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from erpnext.education.api import enroll_student
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'))
diff --git a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.js b/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.js
deleted file mode 100644
index 8d55104..0000000
--- a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Program Enrollment Tool", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Program Enrollment Tool
- () => frappe.tests.make('Program Enrollment Tool', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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
index f22b3b1..55734cb 100644
--- a/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.py
+++ b/erpnext/education/doctype/program_enrollment_tool/test_program_enrollment_tool.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestProgramEnrollmentTool(unittest.TestCase):
pass
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
index 38dc1c8..6765345 100644
--- 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
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProgramEnrollmentToolStudent(Document):
pass
diff --git a/erpnext/education/doctype/program_fee/program_fee.py b/erpnext/education/doctype/program_fee/program_fee.py
index cadcc4e..70105ee 100644
--- a/erpnext/education/doctype/program_fee/program_fee.py
+++ b/erpnext/education/doctype/program_fee/program_fee.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProgramFee(Document):
pass
diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py
index a7deeab..e74aa93 100644
--- a/erpnext/education/doctype/question/question.py
+++ b/erpnext/education/doctype/question/question.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class Question(Document):
def validate(self):
@@ -43,4 +45,4 @@
elif len(answers) == 1:
return answers[0]
else:
- return answers
\ No newline at end of file
+ return answers
diff --git a/erpnext/education/doctype/question/test_question.js b/erpnext/education/doctype/question/test_question.js
deleted file mode 100644
index 509939c..0000000
--- a/erpnext/education/doctype/question/test_question.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Question", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Question
- () => frappe.tests.make('Question', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/question/test_question.py b/erpnext/education/doctype/question/test_question.py
index 552872e..1ce10c0 100644
--- a/erpnext/education/doctype/question/test_question.py
+++ b/erpnext/education/doctype/question/test_question.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuestion(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/quiz/quiz.js b/erpnext/education/doctype/quiz/quiz.js
index 01bcf73..320869b 100644
--- a/erpnext/education/doctype/quiz/quiz.js
+++ b/erpnext/education/doctype/quiz/quiz.js
@@ -68,4 +68,4 @@
method: 'erpnext.education.doctype.quiz.quiz.get_topics_without_quiz',
args: {'quiz': quiz}
});
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py
index a774b88..474bea1 100644
--- a/erpnext/education/doctype/quiz/quiz.py
+++ b/erpnext/education/doctype/quiz/quiz.py
@@ -3,11 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import json
from frappe import _
from frappe.model.document import Document
+
class Quiz(Document):
def validate(self):
if self.passing_score > 100:
@@ -68,4 +69,4 @@
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
\ No newline at end of file
+ return data
diff --git a/erpnext/education/doctype/quiz/test_quiz.js b/erpnext/education/doctype/quiz/test_quiz.js
deleted file mode 100644
index 147d139..0000000
--- a/erpnext/education/doctype/quiz/test_quiz.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quiz", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quiz
- () => frappe.tests.make('Quiz', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/quiz/test_quiz.py b/erpnext/education/doctype/quiz/test_quiz.py
index 344fd54..22eb23d 100644
--- a/erpnext/education/doctype/quiz/test_quiz.py
+++ b/erpnext/education/doctype/quiz/test_quiz.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuiz(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.py b/erpnext/education/doctype/quiz_activity/quiz_activity.py
index 24c7175..0fc7603 100644
--- a/erpnext/education/doctype/quiz_activity/quiz_activity.py
+++ b/erpnext/education/doctype/quiz_activity/quiz_activity.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class QuizActivity(Document):
pass
diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.js b/erpnext/education/doctype/quiz_activity/test_quiz_activity.js
deleted file mode 100644
index 94b5ab7..0000000
--- a/erpnext/education/doctype/quiz_activity/test_quiz_activity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quiz Activity", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quiz Activity
- () => frappe.tests.make('Quiz Activity', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
index fb0425d..44e3a3f 100644
--- a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
+++ b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuizActivity(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/quiz_question/quiz_question.py b/erpnext/education/doctype/quiz_question/quiz_question.py
index 317e75b..20cb9f7 100644
--- a/erpnext/education/doctype/quiz_question/quiz_question.py
+++ b/erpnext/education/doctype/quiz_question/quiz_question.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class QuizQuestion(Document):
pass
diff --git a/erpnext/education/doctype/quiz_result/quiz_result.py b/erpnext/education/doctype/quiz_result/quiz_result.py
index a4fd9f0..059d294 100644
--- a/erpnext/education/doctype/quiz_result/quiz_result.py
+++ b/erpnext/education/doctype/quiz_result/quiz_result.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class QuizResult(Document):
pass
diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.js b/erpnext/education/doctype/quiz_result/test_quiz_result.js
deleted file mode 100644
index 43f53a1..0000000
--- a/erpnext/education/doctype/quiz_result/test_quiz_result.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quiz Result", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quiz Result
- () => frappe.tests.make('Quiz Result', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.py b/erpnext/education/doctype/quiz_result/test_quiz_result.py
index 86ee52d..08ac481 100644
--- a/erpnext/education/doctype/quiz_result/test_quiz_result.py
+++ b/erpnext/education/doctype/quiz_result/test_quiz_result.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuizResult(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/room/room.js b/erpnext/education/doctype/room/room.js
index 20cee6b..1263b60 100644
--- a/erpnext/education/doctype/room/room.js
+++ b/erpnext/education/doctype/room/room.js
@@ -1,2 +1,2 @@
frappe.ui.form.on("Room", {
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/room/room.py b/erpnext/education/doctype/room/room.py
index f26e9c4..dc68a0d 100644
--- a/erpnext/education/doctype/room/room.py
+++ b/erpnext/education/doctype/room/room.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
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
index 99aac33..6a43b60 100644
--- a/erpnext/education/doctype/room/room_dashboard.py
+++ b/erpnext/education/doctype/room/room_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'room',
@@ -16,4 +18,4 @@
'items': ['Assessment Plan']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/room/test_room.py b/erpnext/education/doctype/room/test_room.py
index 33ffd91..5718d51 100644
--- a/erpnext/education/doctype/room/test_room.py
+++ b/erpnext/education/doctype/room/test_room.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Room')
diff --git a/erpnext/education/doctype/school_house/school_house.py b/erpnext/education/doctype/school_house/school_house.py
index 8751e5c..2f9c5f4 100644
--- a/erpnext/education/doctype/school_house/school_house.py
+++ b/erpnext/education/doctype/school_house/school_house.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SchoolHouse(Document):
pass
diff --git a/erpnext/education/doctype/school_house/test_school_house.js b/erpnext/education/doctype/school_house/test_school_house.js
deleted file mode 100644
index dde63ec..0000000
--- a/erpnext/education/doctype/school_house/test_school_house.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: School House", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new School House
- () => frappe.tests.make('School House', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/school_house/test_school_house.py b/erpnext/education/doctype/school_house/test_school_house.py
index 5cf96d5..b58b7d4 100644
--- a/erpnext/education/doctype/school_house/test_school_house.py
+++ b/erpnext/education/doctype/school_house/test_school_house.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestSchoolHouse(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js
index fd23ae4..aead556 100644
--- a/erpnext/education/doctype/student/student.js
+++ b/erpnext/education/doctype/student/student.js
@@ -60,4 +60,4 @@
return { filters: [['Student', 'name', 'not in', sibling_list]] };
};
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 6be9e71..be4ee56 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -3,12 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
-from frappe.utils import getdate,today
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]))
@@ -134,7 +138,9 @@
enrollment.submit()
return enrollment
- def enroll_in_course(self, course_name, program_enrollment, enrollment_date=frappe.utils.datetime.datetime.now()):
+ def enroll_in_course(self, course_name, program_enrollment, enrollment_date=None):
+ if enrollment_date is None:
+ enrollment_date = frappe.utils.datetime.datetime.now()
try:
enrollment = frappe.get_doc({
"doctype": "Course Enrollment",
diff --git a/erpnext/education/doctype/student/student_dashboard.py b/erpnext/education/doctype/student/student_dashboard.py
index d261462..efff2e6 100644
--- a/erpnext/education/doctype/student/student_dashboard.py
+++ b/erpnext/education/doctype/student/student_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/education/doctype/student/student_list.js b/erpnext/education/doctype/student/student_list.js
index 763f120..c1bce24 100644
--- a/erpnext/education/doctype/student/student_list.js
+++ b/erpnext/education/doctype/student/student_list.js
@@ -1,3 +1,3 @@
frappe.listview_settings['Student'] = {
add_fields: [ "image"]
-}
\ No newline at end of file
+}
diff --git a/erpnext/education/doctype/student/test_student.js b/erpnext/education/doctype/student/test_student.js
deleted file mode 100644
index e18d39a..0000000
--- a/erpnext/education/doctype/student/test_student.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Student", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Student
- () => frappe.tests.make('Student', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/student/test_student.py b/erpnext/education/doctype/student/test_student.py
index 2e52637..ec6abb5 100644
--- a/erpnext/education/doctype/student/test_student.py
+++ b/erpnext/education/doctype/student/test_student.py
@@ -2,12 +2,12 @@
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
-from frappe.test_runner import make_test_records
-from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
-from erpnext.education.doctype.course.test_course import make_course
+
+import unittest
import frappe
-import unittest
+
+from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
test_records = frappe.get_test_records('Student')
class TestStudent(unittest.TestCase):
@@ -68,4 +68,4 @@
student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name
return frappe.get_doc("Student", student_id)
except IndexError:
- return None
\ No newline at end of file
+ return None
diff --git a/erpnext/education/doctype/student_admission/student_admission.py b/erpnext/education/doctype/student_admission/student_admission.py
index 0febb96..67ef67b 100644
--- a/erpnext/education/doctype/student_admission/student_admission.py
+++ b/erpnext/education/doctype/student_admission/student_admission.py
@@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import nowdate
diff --git a/erpnext/education/doctype/student_admission/templates/student_admission_row.html b/erpnext/education/doctype/student_admission/templates/student_admission_row.html
index 99868d5..529d651 100644
--- a/erpnext/education/doctype/student_admission/templates/student_admission_row.html
+++ b/erpnext/education/doctype/student_admission/templates/student_admission_row.html
@@ -41,4 +41,4 @@
</div>
</div>
</a>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js
index 3a0bb0b..e01791a 100644
--- a/erpnext/education/doctype/student_admission/test_student_admission.js
+++ b/erpnext/education/doctype/student_admission/test_student_admission.js
@@ -37,4 +37,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student_admission/test_student_admission.py b/erpnext/education/doctype/student_admission/test_student_admission.py
index 748c7ae..c9cfbca 100644
--- a/erpnext/education/doctype/student_admission/test_student_admission.py
+++ b/erpnext/education/doctype/student_admission/test_student_admission.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Admission')
diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.py b/erpnext/education/doctype/student_admission_program/student_admission_program.py
index 406027c..2377d26 100644
--- a/erpnext/education/doctype/student_admission_program/student_admission_program.py
+++ b/erpnext/education/doctype/student_admission_program/student_admission_program.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class StudentAdmissionProgram(Document):
pass
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.js b/erpnext/education/doctype/student_applicant/student_applicant.js
index b4cfdf1..7b41a72 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant.js
+++ b/erpnext/education/doctype/student_applicant/student_applicant.js
@@ -59,4 +59,4 @@
frm.add_fetch("student", "gender", "gender");
frm.add_fetch("student", "date_of_birth", "date_of_birth");
}
-});
\ 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
index 2113482..36a0757 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant.py
+++ b/erpnext/education/doctype/student_applicant/student_applicant.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import print_function, unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import getdate, add_years, nowdate, date_diff
+from frappe.utils import add_years, date_diff, getdate, nowdate
+
class StudentApplicant(Document):
def autoname(self):
@@ -49,7 +51,7 @@
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 \
diff --git a/erpnext/education/doctype/student_applicant/student_applicant_list.js b/erpnext/education/doctype/student_applicant/student_applicant_list.js
index 817a728..c39d46e 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant_list.js
+++ b/erpnext/education/doctype/student_applicant/student_applicant_list.js
@@ -18,4 +18,4 @@
return [__("Admitted"), "blue", "application_status,=,Admitted"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/student_applicant/test_student_applicant.py b/erpnext/education/doctype/student_applicant/test_student_applicant.py
index 9734a88..b7258a4 100644
--- a/erpnext/education/doctype/student_applicant/test_student_applicant.py
+++ b/erpnext/education/doctype/student_applicant/test_student_applicant.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Applicant')
diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js
index a69ad8a..fa67977 100644
--- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js
+++ b/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js
@@ -92,4 +92,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
index 26244ab..03101e4 100644
--- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
+++ b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js
@@ -84,4 +84,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js
index 114358f..daa36e7 100644
--- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js
+++ b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js
@@ -107,4 +107,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.js b/erpnext/education/doctype/student_attendance/student_attendance.js
index f025a1a..2bbecb9 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.js
+++ b/erpnext/education/doctype/student_attendance/student_attendance.js
@@ -2,4 +2,4 @@
// 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")
\ No newline at end of file
+cur_frm.add_fetch("course_schedule", "student_group", "student_group")
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py
index 2e9e6cf..3826afb 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance.py
@@ -3,14 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
-from frappe.utils import get_link_to_form, getdate, formatdate
+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()
diff --git a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py b/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
index 9c41b8f..489f64d 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'reports': [
@@ -9,4 +11,4 @@
'items': ['Student Monthly Attendance Sheet', 'Student Batch-Wise Attendance']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/student_attendance/student_attendance_list.js b/erpnext/education/doctype/student_attendance/student_attendance_list.js
index 0d3e7ad..e89b76c 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance_list.js
+++ b/erpnext/education/doctype/student_attendance/student_attendance_list.js
@@ -8,4 +8,4 @@
return [__("Present"), "green", "status,=,Present"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.js b/erpnext/education/doctype/student_attendance/test_student_attendance.js
index c7da6f6..3d30b09 100644
--- a/erpnext/education/doctype/student_attendance/test_student_attendance.js
+++ b/erpnext/education/doctype/student_attendance/test_student_attendance.js
@@ -28,4 +28,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.py b/erpnext/education/doctype/student_attendance/test_student_attendance.py
index 9f41538..d453aed 100644
--- a/erpnext/education/doctype/student_attendance/test_student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/test_student_attendance.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Attendance')
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
index 028db91..96767de 100644
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
+++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class StudentAttendanceTool(Document):
pass
@@ -38,4 +40,4 @@
if student.student == attendance.student:
student.status = attendance.status
- return student_list
\ No newline at end of file
+ return student_list
diff --git a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js
index cea0761..b66d839 100644
--- a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js
+++ b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js
@@ -82,4 +82,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
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
index ffc42af..a94a3f2 100644
--- a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py
+++ b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestStudentAttendanceTool(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/student_batch_name/student_batch_name.py b/erpnext/education/doctype/student_batch_name/student_batch_name.py
index e6d38ea..ce50716 100644
--- a/erpnext/education/doctype/student_batch_name/student_batch_name.py
+++ b/erpnext/education/doctype/student_batch_name/student_batch_name.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentBatchName(Document):
pass
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
index 09534f3..75ebeb2 100644
--- a/erpnext/education/doctype/student_batch_name/test_student_batch_name.py
+++ b/erpnext/education/doctype/student_batch_name/test_student_batch_name.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Batch Name')
diff --git a/erpnext/education/doctype/student_category/student_category.py b/erpnext/education/doctype/student_category/student_category.py
index bd3a835..bb362d5 100644
--- a/erpnext/education/doctype/student_category/student_category.py
+++ b/erpnext/education/doctype/student_category/student_category.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
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
index f31c34b..9238623 100644
--- a/erpnext/education/doctype/student_category/student_category_dashboard.py
+++ b/erpnext/education/doctype/student_category/student_category_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'student_category',
diff --git a/erpnext/education/doctype/student_category/test_student_category.py b/erpnext/education/doctype/student_category/test_student_category.py
index 756cab8..0893769 100644
--- a/erpnext/education/doctype/student_category/test_student_category.py
+++ b/erpnext/education/doctype/student_category/test_student_category.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Category')
diff --git a/erpnext/education/doctype/student_group/student_group.js b/erpnext/education/doctype/student_group/student_group.js
index 51e3b74..39ee9ce 100644
--- a/erpnext/education/doctype/student_group/student_group.js
+++ b/erpnext/education/doctype/student_group/student_group.js
@@ -142,4 +142,4 @@
return { filters: [['Instructor', 'name', 'not in', instructor_list]] };
};
}
-});
\ 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
index 0260b80..2347d31 100644
--- a/erpnext/education/doctype/student_group/student_group.py
+++ b/erpnext/education/doctype/student_group/student_group.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
-from erpnext.education.utils import validate_duplicate_student
+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()
@@ -128,4 +131,3 @@
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
index ad7a6de..36329bd 100644
--- a/erpnext/education/doctype/student_group/student_group_dashboard.py
+++ b/erpnext/education/doctype/student_group/student_group_dashboard.py
@@ -1,8 +1,10 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'student_group',
@@ -16,4 +18,4 @@
'items': ['Course Schedule']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/student_group/test_student_group.js b/erpnext/education/doctype/student_group/test_student_group.js
index 6673343..4c7e47b 100644
--- a/erpnext/education/doctype/student_group/test_student_group.js
+++ b/erpnext/education/doctype/student_group/test_student_group.js
@@ -53,4 +53,4 @@
() => done()
]);
-});
\ 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
index 8b9b47d..0602251 100644
--- a/erpnext/education/doctype/student_group/test_student_group.py
+++ b/erpnext/education/doctype/student_group/test_student_group.py
@@ -3,10 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
import erpnext.education
+
def get_random_group():
doc = frappe.get_doc({
"doctype": "Student Group",
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
index d0d7afd..c189e27 100644
--- 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
@@ -37,4 +37,4 @@
}
};
});
-});
\ 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
index dc8667e..2007f8a 100644
--- 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
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
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):
@@ -76,4 +79,4 @@
student_group.append('students', student)
student_group.save()
- frappe.msgprint(_("{0} Student Groups created.").format(l))
\ No newline at end of file
+ frappe.msgprint(_("{0} Student Groups created.").format(l))
diff --git a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js
index 34c1093..fa612ba 100644
--- a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js
+++ b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js
@@ -81,4 +81,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
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
index 9ca5658..432da09 100644
--- 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
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestStudentGroupCreationTool(unittest.TestCase):
pass
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
index b3411ea..b9d1e0d 100644
--- 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
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentGroupCreationToolCourse(Document):
pass
diff --git a/erpnext/education/doctype/student_group_instructor/student_group_instructor.py b/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
index b6cc588..81a7ed2 100644
--- a/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
+++ b/erpnext/education/doctype/student_group_instructor/student_group_instructor.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentGroupInstructor(Document):
pass
diff --git a/erpnext/education/doctype/student_group_student/student_group_student.py b/erpnext/education/doctype/student_group_student/student_group_student.py
index 820e301..7ee4cae 100644
--- a/erpnext/education/doctype/student_group_student/student_group_student.py
+++ b/erpnext/education/doctype/student_group_student/student_group_student.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentGroupStudent(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/education/doctype/student_guardian/student_guardian.py b/erpnext/education/doctype/student_guardian/student_guardian.py
index 04445bc..56d7df7 100644
--- a/erpnext/education/doctype/student_guardian/student_guardian.py
+++ b/erpnext/education/doctype/student_guardian/student_guardian.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentGuardian(Document):
pass
diff --git a/erpnext/education/doctype/student_language/student_language.py b/erpnext/education/doctype/student_language/student_language.py
index be6d5de..6ec0b1f 100644
--- a/erpnext/education/doctype/student_language/student_language.py
+++ b/erpnext/education/doctype/student_language/student_language.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentLanguage(Document):
pass
diff --git a/erpnext/education/doctype/student_language/test_student_language.js b/erpnext/education/doctype/student_language/test_student_language.js
deleted file mode 100644
index 9b25569..0000000
--- a/erpnext/education/doctype/student_language/test_student_language.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Student Language", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Student Language
- () => frappe.tests.make('Student Language', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/student_language/test_student_language.py b/erpnext/education/doctype/student_language/test_student_language.py
index 592b94a..1d7c003 100644
--- a/erpnext/education/doctype/student_language/test_student_language.py
+++ b/erpnext/education/doctype/student_language/test_student_language.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Language')
diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application.py b/erpnext/education/doctype/student_leave_application/student_leave_application.py
index ef67012..50c14aa 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application.py
@@ -3,13 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from datetime import timedelta
+
import frappe
from frappe import _
-from datetime import timedelta
-from frappe.utils import get_link_to_form, getdate, date_diff, flt
-from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
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):
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
index fdcc147..2674f54 100644
--- a/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py
+++ b/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'leave_application',
@@ -8,4 +9,4 @@
'items': ['Student Attendance']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js b/erpnext/education/doctype/student_leave_application/test_student_leave_application.js
index 5af9f5d..6bbf17b 100644
--- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js
+++ b/erpnext/education/doctype/student_leave_application/test_student_leave_application.js
@@ -66,4 +66,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
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
index fcdd428..506dc73 100644
--- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
+++ b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py
@@ -3,12 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import getdate, add_days, add_months
+
+import frappe
+from frappe.utils import add_days, add_months, getdate
+
from erpnext import get_default_company
-from erpnext.education.doctype.student_group.test_student_group import get_random_group
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):
@@ -112,4 +115,4 @@
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
\ No newline at end of file
+ return holiday_list
diff --git a/erpnext/education/doctype/student_log/student_log.py b/erpnext/education/doctype/student_log/student_log.py
index 8b12886..2ca49ca 100644
--- a/erpnext/education/doctype/student_log/student_log.py
+++ b/erpnext/education/doctype/student_log/student_log.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentLog(Document):
pass
diff --git a/erpnext/education/doctype/student_log/test_student_log.js b/erpnext/education/doctype/student_log/test_student_log.js
index 5775369..4c90c5f 100644
--- a/erpnext/education/doctype/student_log/test_student_log.js
+++ b/erpnext/education/doctype/student_log/test_student_log.js
@@ -32,4 +32,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/education/doctype/student_log/test_student_log.py b/erpnext/education/doctype/student_log/test_student_log.py
index 1fe191f..533191f 100644
--- a/erpnext/education/doctype/student_log/test_student_log.py
+++ b/erpnext/education/doctype/student_log/test_student_log.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Student Log')
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
index 72772b7..a9e84e6 100644
--- 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
@@ -12,67 +12,67 @@
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;
@@ -83,88 +83,88 @@
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;
@@ -173,16 +173,16 @@
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 {
@@ -344,7 +344,7 @@
<br>
<div class="row section-break pbi_avoid">
- <div class="col-xs-6 column-break">
+ <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
@@ -352,7 +352,7 @@
</div>
</div>
- <div class="col-xs-6 column-break">
+ <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' }}
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
index 17bc367..1cf7921 100644
--- 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
@@ -3,13 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
+
+import json
+
+import frappe
from frappe import _
from frappe.model.document import Document
-from erpnext.education.api import get_grade
from frappe.utils.pdf import get_pdf
-from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_formatted_result
-from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_child_assessment_groups
+
+from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import (
+ get_child_assessment_groups,
+ get_formatted_result,
+)
class StudentReportGenerationTool(Document):
diff --git a/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.js b/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.js
deleted file mode 100644
index 10be092..0000000
--- a/erpnext/education/doctype/student_report_generation_tool/test_student_report_generation_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Student Report Generation Tool", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Student Report Generation Tool
- () => frappe.tests.make('Student Report Generation Tool', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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
index 4178166..f622713 100644
--- 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
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestStudentReportGenerationTool(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/student_sibling/student_sibling.py b/erpnext/education/doctype/student_sibling/student_sibling.py
index 4adc3f3..b36cf59 100644
--- a/erpnext/education/doctype/student_sibling/student_sibling.py
+++ b/erpnext/education/doctype/student_sibling/student_sibling.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentSibling(Document):
pass
diff --git a/erpnext/education/doctype/student_siblings/student_siblings.py b/erpnext/education/doctype/student_siblings/student_siblings.py
index 4e20d84..412cf05 100644
--- a/erpnext/education/doctype/student_siblings/student_siblings.py
+++ b/erpnext/education/doctype/student_siblings/student_siblings.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StudentSiblings(Document):
pass
diff --git a/erpnext/education/doctype/topic/test_topic.js b/erpnext/education/doctype/topic/test_topic.js
deleted file mode 100644
index 4460b79..0000000
--- a/erpnext/education/doctype/topic/test_topic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Topic", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Topic
- () => frappe.tests.make('Topic', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/topic/test_topic.py b/erpnext/education/doctype/topic/test_topic.py
index d03db1c..b6c6c75 100644
--- a/erpnext/education/doctype/topic/test_topic.py
+++ b/erpnext/education/doctype/topic/test_topic.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
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"}])
diff --git a/erpnext/education/doctype/topic/topic.js b/erpnext/education/doctype/topic/topic.js
index 2002b0c..0c903c5 100644
--- a/erpnext/education/doctype/topic/topic.js
+++ b/erpnext/education/doctype/topic/topic.js
@@ -52,4 +52,4 @@
method: 'erpnext.education.doctype.topic.topic.get_courses_without_topic',
args: {'topic': topic}
});
-};
\ No newline at end of file
+};
diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py
index a5253e9..1834b2e 100644
--- a/erpnext/education/doctype/topic/topic.py
+++ b/erpnext/education/doctype/topic/topic.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
from frappe import _
from frappe.model.document import Document
+
class Topic(Document):
def get_contents(self):
try:
@@ -56,4 +59,4 @@
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')
\ No newline at end of file
+ title=_('Topics updated'), indicator='green')
diff --git a/erpnext/education/doctype/topic_content/test_topic_content.js b/erpnext/education/doctype/topic_content/test_topic_content.js
deleted file mode 100644
index bf9a62d..0000000
--- a/erpnext/education/doctype/topic_content/test_topic_content.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Topic Content", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Topic Content
- () => frappe.tests.make('Topic Content', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/topic_content/test_topic_content.py b/erpnext/education/doctype/topic_content/test_topic_content.py
index cf304f6..6fdcbda 100644
--- a/erpnext/education/doctype/topic_content/test_topic_content.py
+++ b/erpnext/education/doctype/topic_content/test_topic_content.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestTopicContent(unittest.TestCase):
pass
diff --git a/erpnext/education/doctype/topic_content/topic_content.py b/erpnext/education/doctype/topic_content/topic_content.py
index 9b2c90b..9339bbd 100644
--- a/erpnext/education/doctype/topic_content/topic_content.py
+++ b/erpnext/education/doctype/topic_content/topic_content.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TopicContent(Document):
pass
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.py b/erpnext/education/report/absent_student_report/absent_student_report.py
index c3487cc..d5b6675 100644
--- a/erpnext/education/report/absent_student_report/absent_student_report.py
+++ b/erpnext/education/report/absent_student_report/absent_student_report.py
@@ -2,12 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _, msgprint
from frappe.utils import formatdate
-from frappe import msgprint, _
+
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 = {}
diff --git a/erpnext/education/report/assessment_plan_status/assessment_plan_status.py b/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
index 21184a6..64ceb42 100644
--- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
+++ b/erpnext/education/report/assessment_plan_status/assessment_plan_status.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from itertools import groupby
+
import frappe
from frappe import _
-from itertools import groupby
from frappe.utils import cint
DOCSTATUS = {
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
index 1043e5b..ad07ee1 100644
--- 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
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from collections import OrderedDict, defaultdict
+
import frappe
from frappe import _
-from frappe.utils import flt
-from collections import defaultdict, OrderedDict
+
from erpnext.education.api import get_grade
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.py b/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
index e6e0ba2..ae7f34b 100644
--- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
+++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
@@ -2,12 +2,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
+
from collections import defaultdict
-from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_formatted_result
-from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_chart_data
+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):
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
index c145359..1717ed5 100644
--- 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if not filters:
filters = {}
@@ -121,4 +123,3 @@
},
'type': 'bar'
}
-
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
index 5bebd43..a21a936 100644
--- 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
@@ -2,6 +2,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
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
index ad04356..9f1fcbc 100644
--- 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
@@ -9,4 +9,4 @@
"default": frappe.datetime.get_today(),
"reqd": 1
}]
-}
\ 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
index 7793dcf..b65350f 100644
--- 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
@@ -2,12 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _, msgprint
from frappe.utils import formatdate
-from frappe import msgprint, _
+
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 = {}
@@ -67,4 +70,4 @@
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
\ No newline at end of file
+ return student_attendance
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
index 104d3ec..62c9455 100644
--- 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
@@ -39,4 +39,4 @@
}
});
}
-}
\ 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
index 04dc8c0..f6d9c5a 100644
--- 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
@@ -2,14 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cstr, cint, getdate, get_first_day, get_last_day, date_diff, add_days
-from frappe import msgprint, _
-from calendar import monthrange
+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 = {}
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
index 3070e6a..33394e1 100644
--- a/erpnext/education/utils.py
+++ b/erpnext/education/utils.py
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
-from __future__ import unicode_literals, division
+from __future__ import division, unicode_literals
+
import frappe
from frappe import _
+
class OverlapError(frappe.ValidationError): pass
def validate_overlap_for(doc, doctype, fieldname, value=None):
@@ -219,7 +221,7 @@
try:
quiz = frappe.get_doc("Quiz", quiz_name)
questions = quiz.get_questions()
- except:
+ except Exception:
frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
return None
diff --git a/erpnext/education/web_form/student_applicant/student_applicant.js b/erpnext/education/web_form/student_applicant/student_applicant.js
index 699703c..ffc5e98 100644
--- a/erpnext/education/web_form/student_applicant/student_applicant.js
+++ b/erpnext/education/web_form/student_applicant/student_applicant.js
@@ -1,3 +1,3 @@
frappe.ready(function() {
// bind events here
-})
\ 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
index 2334f8b..f57de91 100644
--- a/erpnext/education/web_form/student_applicant/student_applicant.py
+++ b/erpnext/education/web_form/student_applicant/student_applicant.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
index a505ee0..192ec14 100644
--- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
+++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
@@ -1,8 +1,15 @@
from __future__ import unicode_literals
-import frappe, base64, hashlib, hmac, json
-from frappe.utils import cstr
+
+import base64
+import hashlib
+import hmac
+import json
+
+import frappe
from frappe import _
+from frappe.utils import cstr
+
def verify_request():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
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
index aeb5352..5a4a57c 100644
--- a/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/__init__.py
+++ b/erpnext/erpnext_integrations/data_migration_mapping/issue_to_task/__init__.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def pre_process(issue):
project = frappe.db.get_value('Project', filters={'project_name': issue.milestone})
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
index 9d3f02e..bab8031 100644
--- a/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/__init__.py
+++ b/erpnext/erpnext_integrations/data_migration_mapping/milestone_to_project/__init__.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def pre_process(milestone):
return {
'title': milestone.title,
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
index 148c1a6..a757a43 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
@@ -3,10 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, time, dateutil, math, csv
-from six import StringIO
-import erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_api as mws
+
+import csv
+import math
+import time
+
+import dateutil
+import frappe
from frappe import _
+from six import StringIO
+
+import erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_api as mws
+
#Get and Create Products
def get_products_details():
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py
index 7fd3b34..652fa92 100755
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py
@@ -6,24 +6,24 @@
# Extended to include finances object
from __future__ import unicode_literals
-import urllib
-from urllib.parse import quote
+import base64
import hashlib
import hmac
-import base64
-import six
-from erpnext.erpnext_integrations.doctype.amazon_mws_settings import xml_utils
import re
+from urllib.parse import quote
+
+from erpnext.erpnext_integrations.doctype.amazon_mws_settings import xml_utils
+
try:
from xml.etree.ElementTree import ParseError as XMLError
except ImportError:
from xml.parsers.expat import ExpatError as XMLError
-from time import strftime, gmtime
+
+from time import gmtime, strftime
from requests import request
from requests.exceptions import HTTPError
-
__all__ = [
'Feeds',
'Inventory',
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js
index a9925ad..f5ea804 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js
@@ -1,3 +1,2 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py
index 9c59840..ac59eb7 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
+
import dateutil
+import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from frappe.model.document import Document
+
from erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods import get_orders
+
class AmazonMWSSettings(Document):
def validate(self):
if self.enable_amazon == 1:
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.js b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.js
deleted file mode 100644
index 9c89909..0000000
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Amazon MWS Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Amazon MWS Settings
- () => frappe.tests.make('Amazon MWS Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py
index 7b40014..844df59 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestAmazonMWSSettings(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py
index a25a29f..88ef64d 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py
@@ -8,8 +8,8 @@
"""
from __future__ import unicode_literals
-import xml.etree.ElementTree as ET
import re
+import xml.etree.ElementTree as ET
class object_dict(dict):
@@ -88,7 +88,7 @@
ns = http://cs.sfsu.edu/csc867/myscheduler
name = patients
"""
- result = re.compile("\{(.*)\}(.*)").search(tag)
+ result = re.compile(r"\{(.*)\}(.*)").search(tag)
if result:
value.namespace, tag = result.groups()
@@ -103,4 +103,4 @@
"""parse a string"""
t = ET.fromstring(s)
root_tag, root_tree = self._namespace_split(t.tag, self._parse_node(t))
- return object_dict({root_tag: root_tree})
\ No newline at end of file
+ return object_dict({root_tag: root_tree})
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
index 6a846ef..f1314fc 100644
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
-import requests
+
import frappe
+import requests
from frappe import _
+from frappe.model.document import Document
+
class ExotelSettings(Document):
def validate(self):
@@ -17,4 +19,4 @@
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"))
\ No newline at end of file
+ frappe.throw(_("Invalid credentials"))
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
index 9c9df65..b416ce8 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class GoCardlessMandate(Document):
pass
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.js b/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.js
deleted file mode 100644
index caa9399..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GoCardless Mandate", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new GoCardless Mandate
- () => frappe.tests.make('GoCardless Mandate', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py b/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
index d77a352..1b76ee5 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
@@ -5,5 +5,6 @@
import unittest
+
class TestGoCardlessMandate(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
index 25784a5..d003edb 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
@@ -3,10 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-import json
-import hmac
+
import hashlib
+import hmac
+import json
+
+import frappe
+
@frappe.whitelist(allow_guest=True)
def webhooks():
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
index c65e3ce..6484973 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
@@ -3,13 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
import gocardless_pro
from frappe import _
+from frappe.integrations.utils import create_payment_gateway, create_request_log
+from frappe.model.document import Document
+from frappe.utils import call_hook_method, cint, flt, get_url
from six.moves.urllib.parse import urlencode
-from frappe.utils import get_url, call_hook_method, flt, cint
-from frappe.integrations.utils import create_request_log, create_payment_gateway
+
class GoCardlessSettings(Document):
supported_currencies = ["EUR", "DKK", "GBP", "SEK"]
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.js b/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.js
deleted file mode 100644
index b6daad8..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GoCardless Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new GoCardless Settings
- () => frappe.tests.make('GoCardless Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
index e377f34..b17aef5 100644
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
+++ b/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestGoCardlessSettings(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
index 2c4d4bb..b74a718 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
@@ -25,4 +25,4 @@
</table>
{% else %}
<p style="margin-top: 30px;"> Account Balance Information Not Available. </p>
-{% endif %}
\ No newline at end of file
+{% endif %}
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
index 554c6b0..6d46a1c 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
@@ -1,7 +1,9 @@
import base64
+import datetime
+
import requests
from requests.auth import HTTPBasicAuth
-import datetime
+
class MpesaConnector():
def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke",
@@ -115,4 +117,4 @@
saf_url = "{0}{1}".format(self.base_url, "/mpesa/stkpush/v1/processrequest")
r = requests.post(saf_url, headers=headers, json=payload)
- return r.json()
\ No newline at end of file
+ return r.json()
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 0499e88..368139b 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
@@ -1,6 +1,7 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
def create_custom_pos_fields():
"""Create custom fields corresponding to POS Settings and POS Invoice."""
pos_field = {
@@ -50,4 +51,4 @@
for record in record_dict:
if frappe.db.exists("POS Field", {"fieldname": record.get("fieldname")}):
continue
- frappe.get_doc(record).insert()
\ No newline at end of file
+ frappe.get_doc(record).insert()
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index fdfaa1b..4ce85e5 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -4,17 +4,21 @@
from __future__ import unicode_literals
-from json import loads, dumps
+
+from json import dumps, loads
import frappe
-from frappe.model.document import Document
from frappe import _
-from frappe.utils import call_hook_method, fmt_money
-from frappe.integrations.utils import create_request_log, create_payment_gateway
-from frappe.utils import get_request_site_address
-from erpnext.erpnext_integrations.utils import create_mode_of_payment
+from frappe.integrations.utils import create_payment_gateway, create_request_log
+from frappe.model.document import Document
+from frappe.utils import call_hook_method, fmt_money, get_request_site_address
+
from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_connector import MpesaConnector
-from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_custom_fields import create_custom_pos_fields
+from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_custom_fields import (
+ create_custom_pos_fields,
+)
+from erpnext.erpnext_integrations.utils import create_mode_of_payment
+
class MpesaSettings(Document):
supported_currencies = ["KES"]
@@ -39,7 +43,9 @@
for i, amount in enumerate(request_amounts):
args.request_amount = amount
if frappe.flags.in_test:
- from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload
+ 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))
@@ -71,7 +77,9 @@
)
if frappe.flags.in_test:
- from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_test_account_balance_response
+ 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))
@@ -276,4 +284,4 @@
"""Fetch the specified key from list of dictionary. Key is identified via the key field."""
for param in response:
if param[key_field] == key:
- return param["Value"]
\ No newline at end of file
+ return param["Value"]
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 b0e662d..de81b82 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -2,13 +2,20 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-from json import dumps
-import frappe
+
import unittest
-from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import process_balance_info, verify_transaction
+from json import dumps
+
+import frappe
+
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import (
+ process_balance_info,
+ verify_transaction,
+)
from erpnext.erpnext_integrations.utils import create_mode_of_payment
+
class TestMpesaSettings(unittest.TestCase):
def setUp(self):
# create payment gateway in setup
@@ -355,4 +362,4 @@
}
}
}
- }
\ No newline at end of file
+ }
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 42d4b9b..d4cf56a 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -2,12 +2,11 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
+import frappe
import plaid
import requests
-from plaid.errors import APIError, ItemError, InvalidRequestError
-
-import frappe
from frappe import _
+from plaid.errors import APIError, InvalidRequestError, ItemError
class PlaidConnector():
@@ -50,7 +49,7 @@
"secret": self.settings.plaid_secret,
"products": self.products,
})
-
+
return args
def get_link_token(self, update_mode=False):
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
index 37bf282..3740d04 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
@@ -135,4 +135,4 @@
});
}, __("Select a company"), __("Continue"));
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 3ef069b..310afed 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -5,15 +5,16 @@
import json
import frappe
-from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
-from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
from frappe import _
from frappe.desk.doctype.tag.tag import add_tag
from frappe.model.document import Document
from frappe.utils import add_months, formatdate, getdate, today
-
from plaid.errors import ItemError
+from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
+from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
+
+
class PlaidSettings(Document):
@staticmethod
@frappe.whitelist()
@@ -84,10 +85,8 @@
if not acc_subtype:
add_account_subtype(account["subtype"])
- existing_bank_account = frappe.db.exists("Bank Account", {
- 'account_name': account["name"],
- 'bank': bank["bank_name"]
- })
+ bank_account_name = "{} - {}".format(account["name"], bank["bank_name"])
+ existing_bank_account = frappe.db.exists("Bank Account", bank_account_name)
if not existing_bank_account:
try:
@@ -110,7 +109,7 @@
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."),
+ frappe.throw(_("There was an error creating Bank Account while linking with Plaid."),
title=_("Plaid Link Failed"))
else:
@@ -196,6 +195,7 @@
plaid = PlaidConnector(access_token)
+ transactions = []
try:
transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
except ItemError as e:
@@ -204,7 +204,7 @@
msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " "
frappe.log_error(msg, title=_("Plaid Link Refresh Required"))
- return transactions or []
+ return transactions
def new_bank_transaction(transaction):
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js
deleted file mode 100644
index dc91347..0000000
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Plaid Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Plaid Settings
- () => frappe.tests.make('Plaid Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 e2243ea..32b5b8f 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
@@ -6,11 +6,16 @@
import unittest
import frappe
+from frappe.utils.response import json_handler
+
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import (
- add_account_subtype, add_account_type, add_bank_accounts,
- new_bank_transaction, get_plaid_configuration)
-from frappe.utils.response import json_handler
+ add_account_subtype,
+ add_account_type,
+ add_bank_accounts,
+ get_plaid_configuration,
+ new_bank_transaction,
+)
class TestPlaidSettings(unittest.TestCase):
diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
index 866ea66..39b9bb2 100644
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
+++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py
@@ -3,15 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import json
+import traceback
+
import frappe
+import requests
from frappe import _
from frappe.model.document import Document
from requests_oauthlib import OAuth2Session
-import json
-import requests
-import traceback
+
from erpnext import encode_company_abbr
+
# QuickBooks requires a redirect URL, User will be redirect to this URL
# This will be a GET request
# Request parameters will have two parameters `code` and `realmId`
@@ -253,8 +257,9 @@
try:
# Assumes that exactly one fiscal year has been created so far
# Creates fiscal years till oldest ledger entry date is covered
- from frappe.utils.data import add_years, getdate
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"],
diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.js b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.js
deleted file mode 100644
index b71d704..0000000
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: QuickBooks Migrator", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new QuickBooks Migrator
- () => frappe.tests.make('QuickBooks Migrator', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py
index 6ce7c92..5604b40 100644
--- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py
+++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/test_quickbooks_migrator.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQuickBooksMigrator(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js
index 5482b9c..af06b34 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js
@@ -346,4 +346,4 @@
}).join("");
return rows
-}
\ No newline at end of file
+}
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index 907a223..e1e7f62 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -11,19 +11,19 @@
import zipfile
from decimal import Decimal
-from bs4 import BeautifulSoup as bs
-
import frappe
-from erpnext import encode_company_abbr
-from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
-from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data
-
+from bs4 import BeautifulSoup as bs
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.model.document import Document
-from frappe.model.naming import getseries, revert_series_if_last
from frappe.utils.data import format_datetime
+from erpnext import encode_company_abbr
+from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
+from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import (
+ unset_existing_data,
+)
+
PRIMARY_ACCOUNT = "Primary"
VOUCHER_CHUNK_SIZE = 500
@@ -266,7 +266,7 @@
self.is_master_data_processed = 1
- except:
+ except Exception:
self.publish("Process Master Data", _("Process Failed"), -1, 5)
self.log()
@@ -302,14 +302,14 @@
try:
party_doc = frappe.get_doc(party)
party_doc.insert()
- except:
+ except Exception:
self.log(party_doc)
addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url})
for address in json.loads(addresses_file.get_content()):
try:
address_doc = frappe.get_doc(address)
address_doc.insert(ignore_mandatory=True)
- except:
+ except Exception:
self.log(address_doc)
def create_items_uoms(items_file_url, uoms_file_url):
@@ -319,7 +319,7 @@
try:
uom_doc = frappe.get_doc(uom)
uom_doc.insert()
- except:
+ except Exception:
self.log(uom_doc)
items_file = frappe.get_doc("File", {"file_url": items_file_url})
@@ -327,7 +327,7 @@
try:
item_doc = frappe.get_doc(item)
item_doc.insert()
- except:
+ except Exception:
self.log(item_doc)
try:
@@ -346,7 +346,7 @@
self.is_master_data_imported = 1
frappe.db.commit()
- except:
+ except Exception:
self.publish("Import Master Data", _("Process Failed"), -1, 5)
frappe.db.rollback()
self.log()
@@ -370,7 +370,7 @@
if processed_voucher:
vouchers.append(processed_voucher)
frappe.db.commit()
- except:
+ except Exception:
frappe.db.rollback()
self.log(voucher)
return vouchers
@@ -494,7 +494,7 @@
self.is_day_book_data_processed = 1
- except:
+ except Exception:
self.publish("Process Day Book Data", _("Process Failed"), -1, 5)
self.log()
@@ -564,7 +564,7 @@
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)
- except:
+ except Exception:
self.log()
finally:
@@ -583,7 +583,7 @@
voucher_doc.submit()
self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total)
frappe.db.commit()
- except:
+ except Exception:
frappe.db.rollback()
self.log(voucher_doc)
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js
deleted file mode 100644
index 433c5e2..0000000
--- a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Tally Migration", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Tally Migration
- () => frappe.tests.make('Tally Migration', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py
index 9f67e55..aae8f6d 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/test_tally_migration.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestTallyMigration(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
index 7f5f0f0..9dd4817 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class TaxJarSettings(Document):
pass
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py
index 7cdfd00..c871b05 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestTaxJarSettings(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.js b/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.js
deleted file mode 100644
index ea06ab2..0000000
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Woocommerce Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Woocommerce Settings
- () => frappe.tests.make('Woocommerce Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.py
index 458a23f..3d18458 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/test_woocommerce_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestWoocommerceSettings(unittest.TestCase):
pass
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
index bd072f4..2e15fab 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils.nestedset import get_root_of
-from frappe.model.document import Document
-from six.moves.urllib.parse import urlparse
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.model.document import Document
+from frappe.utils.nestedset import get_root_of
+from six.moves.urllib.parse import urlparse
+
class WoocommerceSettings(Document):
def validate(self):
@@ -27,7 +29,7 @@
for doctype in ["Customer", "Address"]:
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")}):
item_group = frappe.new_doc("Item Group")
item_group.item_group_name = _("WooCommerce Products")
@@ -74,4 +76,4 @@
def get_series():
return {
"sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-WOO-",
- }
\ No newline at end of file
+ }
diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py
index a35ca28..f0315eb 100644
--- a/erpnext/erpnext_integrations/stripe_integration.py
+++ b/erpnext/erpnext_integrations/stripe_integration.py
@@ -2,11 +2,11 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
import frappe
+import stripe
from frappe import _
from frappe.integrations.utils import create_request_log
-import stripe
+
def create_stripe_subscription(gateway_controller, data):
stripe_settings = frappe.get_doc("Stripe Settings", gateway_controller)
@@ -23,31 +23,38 @@
except Exception:
frappe.log_error(frappe.get_traceback())
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.")),
+ "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.")
+ ),
"status": 401
}
def create_subscription_on_stripe(stripe_settings):
- items = []
- for payment_plan in stripe_settings.payment_plans:
- plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "payment_plan_id")
- items.append({"plan": plan, "quantity": payment_plan.qty})
+ items = []
+ for payment_plan in stripe_settings.payment_plans:
+ plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "product_price_id")
+ items.append({"price": plan, "quantity": payment_plan.qty})
- try:
- customer = stripe.Customer.create(description=stripe_settings.data.payer_name, email=stripe_settings.data.payer_email, source=stripe_settings.data.stripe_token_id)
- subscription = stripe.Subscription.create(customer=customer, items=items)
+ try:
+ customer = stripe.Customer.create(
+ source=stripe_settings.data.stripe_token_id,
+ description=stripe_settings.data.payer_name,
+ email=stripe_settings.data.payer_email
+ )
- if subscription.status == "active":
- stripe_settings.integration_request.db_set('status', 'Completed', update_modified=False)
- stripe_settings.flags.status_changed_to = "Completed"
+ subscription = stripe.Subscription.create(customer=customer, items=items)
- else:
- stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
- frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed')
+ if subscription.status == "active":
+ stripe_settings.integration_request.db_set('status', 'Completed', update_modified=False)
+ stripe_settings.flags.status_changed_to = "Completed"
- except Exception:
+ else:
stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
- frappe.log_error(frappe.get_traceback())
+ frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed')
+ except Exception:
+ stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
+ frappe.log_error(frappe.get_traceback())
- return stripe_settings.finalize_request()
\ No newline at end of file
+ return stripe_settings.finalize_request()
diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py
index f960998..870a4ef 100644
--- a/erpnext/erpnext_integrations/taxjar_integration.py
+++ b/erpnext/erpnext_integrations/taxjar_integration.py
@@ -1,11 +1,12 @@
import traceback
-import taxjar
-
import frappe
-from erpnext import get_default_company
+import taxjar
from frappe import _
from frappe.contacts.doctype.address.address import get_company_address
+from frappe.utils import cint
+
+from erpnext import get_default_company
TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head")
SHIP_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "shipping_account_head")
@@ -14,6 +15,10 @@
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():
@@ -27,7 +32,11 @@
api_url = taxjar.SANDBOX_API_URL
if api_key and api_url:
- return taxjar.Client(api_key=api_key, api_url=api_url)
+ client = taxjar.Client(api_key=api_key, api_url=api_url)
+ client.set_api_config('headers', {
+ 'x-api-version': '2020-08-07'
+ })
+ return client
def create_transaction(doc, method):
@@ -57,7 +66,10 @@
tax_dict['amount'] = doc.total + tax_dict['shipping']
try:
- client.create_order(tax_dict)
+ if doc.is_return:
+ client.create_refund(tax_dict)
+ else:
+ client.create_order(tax_dict)
except taxjar.exceptions.TaxJarResponseError as err:
frappe.throw(_(sanitize_error_response(err)))
except Exception as ex:
@@ -89,13 +101,15 @@
to_country_code = frappe.db.get_value("Country", to_address.country, "code")
to_country_code = to_country_code.upper()
- if to_country_code not in SUPPORTED_COUNTRY_CODES:
- return
-
shipping = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == SHIP_ACCOUNT_HEAD])
- if to_shipping_state is not None:
- to_shipping_state = get_iso_3166_2_state_code(to_address)
+ line_items = [get_line_item_dict(item) for item in doc.items]
+
+ if from_shipping_state not in SUPPORTED_STATE_CODES:
+ 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')
tax_dict = {
'from_country': from_country_code,
@@ -109,11 +123,29 @@
'to_street': to_address.address_line1,
'to_state': to_shipping_state,
'shipping': shipping,
- 'amount': doc.net_total
+ 'amount': doc.net_total,
+ 'plugin': 'erpnext',
+ '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)
+ if state_code not in SUPPORTED_STATE_CODES:
+ frappe.throw(_("Please enter a valid State in the {0} Address").format(location))
+ else:
+ frappe.throw(_("Please enter a valid State in the {0} Address").format(location))
+
+ return state_code
+
+def get_line_item_dict(item):
+ return dict(
+ id = item.get('idx'),
+ quantity = item.get('qty'),
+ unit_price = item.get('rate'),
+ product_tax_code = item.get('product_tax_category')
+ )
def set_sales_tax(doc, method):
if not TAXJAR_CALCULATE_TAX:
@@ -122,17 +154,7 @@
if not doc.items:
return
- # if the party is exempt from sales tax, then set all tax account heads to zero
- sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
- 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:
- if tax.account_head == TAX_ACCOUNT_HEAD:
- tax.tax_amount = 0
- break
-
- doc.run_method("calculate_taxes_and_totals")
+ if check_sales_tax_exemption(doc):
return
tax_dict = get_tax_data(doc)
@@ -143,7 +165,6 @@
return
tax_data = validate_tax_request(tax_dict)
-
if tax_data is not None:
if not tax_data.amount_to_collect:
setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
@@ -163,9 +184,28 @@
"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.run_method("calculate_taxes_and_totals")
+def check_sales_tax_exemption(doc):
+ # if the party is exempt from sales tax, then set all tax account heads to zero
+ sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
+ 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:
+ if tax.account_head == TAX_ACCOUNT_HEAD:
+ tax.tax_amount = 0
+ break
+ doc.run_method("calculate_taxes_and_totals")
+ return True
+ else:
+ return False
def validate_tax_request(tax_dict):
"""Return the sales tax that should be collected for a given order."""
@@ -200,6 +240,8 @@
if doc.shipping_address_name:
shipping_address = frappe.get_doc("Address", doc.shipping_address_name)
+ elif doc.customer_address:
+ shipping_address = frappe.get_doc("Address", doc.customer_address_name)
else:
shipping_address = get_company_address_details(doc)
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index a5e162f..bb5c0c2 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -1,10 +1,16 @@
from __future__ import unicode_literals
+
+import base64
+import hashlib
+import hmac
+
import frappe
from frappe import _
-import base64, hashlib, hmac
from six.moves.urllib.parse import urlparse
+
from erpnext import get_default_company
+
def validate_webhooks_request(doctype, hmac_key, secret_key='secret'):
def innerfn(fn):
settings = frappe.get_doc(doctype)
@@ -52,7 +58,7 @@
"payment_gateway": gateway
}, ['payment_account'])
- mode_of_payment = frappe.db.exists("Mode of Payment", gateway)
+ 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",
diff --git a/erpnext/exceptions.py b/erpnext/exceptions.py
index 04291cd..9c6b13f 100644
--- a/erpnext/exceptions.py
+++ b/erpnext/exceptions.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
# accounts
class PartyFrozen(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
diff --git a/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json b/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json
deleted file mode 100644
index a59f149..0000000
--- a/erpnext/healthcare/dashboard_chart/clinical_procedures/clinical_procedures.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "chart_name": "Clinical Procedures",
- "chart_type": "Group By",
- "creation": "2020-07-14 18:17:54.601236",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Clinical Procedure",
- "dynamic_filters_json": "[[\"Clinical Procedure\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[[\"Clinical Procedure\",\"docstatus\",\"=\",\"1\",false]]",
- "group_by_based_on": "procedure_template",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:47.008622",
- "modified": "2020-07-22 13:36:48.114479",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Clinical Procedures",
- "number_of_groups": 0,
- "owner": "Administrator",
- "timeseries": 0,
- "type": "Percentage",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json b/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json
deleted file mode 100644
index 6d560f7..0000000
--- a/erpnext/healthcare/dashboard_chart/clinical_procedures_status/clinical_procedures_status.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "chart_name": "Clinical Procedure Status",
- "chart_type": "Group By",
- "creation": "2020-07-14 18:17:54.654325",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Clinical Procedure",
- "dynamic_filters_json": "[[\"Clinical Procedure\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[[\"Clinical Procedure\",\"docstatus\",\"=\",\"1\",false]]",
- "group_by_based_on": "status",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:46.691764",
- "modified": "2020-07-22 13:40:17.215775",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Clinical Procedures Status",
- "number_of_groups": 0,
- "owner": "Administrator",
- "timeseries": 0,
- "type": "Pie",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/department_wise_patient_appointments/department_wise_patient_appointments.json b/erpnext/healthcare/dashboard_chart/department_wise_patient_appointments/department_wise_patient_appointments.json
deleted file mode 100644
index b24bb34..0000000
--- a/erpnext/healthcare/dashboard_chart/department_wise_patient_appointments/department_wise_patient_appointments.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "chart_name": "Department wise Patient Appointments",
- "chart_type": "Custom",
- "creation": "2020-07-17 11:25:37.190130",
- "custom_options": "{\"colors\": [\"#7CD5FA\", \"#5F62F6\", \"#7544E2\", \"#EE5555\"], \"barOptions\": {\"stacked\": 1}, \"height\": 300}",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}",
- "filters_json": "{}",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 15:32:05.827566",
- "modified": "2020-07-22 15:35:12.798035",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Department wise Patient Appointments",
- "number_of_groups": 0,
- "owner": "Administrator",
- "source": "Department wise Patient Appointments",
- "timeseries": 0,
- "type": "Bar",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json b/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json
deleted file mode 100644
index 0195aac..0000000
--- a/erpnext/healthcare/dashboard_chart/diagnoses/diagnoses.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "chart_name": "Diagnoses",
- "chart_type": "Group By",
- "creation": "2020-07-14 18:17:54.705698",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Patient Encounter Diagnosis",
- "filters_json": "[]",
- "group_by_based_on": "diagnosis",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:47.895521",
- "modified": "2020-07-22 13:43:32.369481",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Diagnoses",
- "number_of_groups": 0,
- "owner": "Administrator",
- "timeseries": 0,
- "type": "Percentage",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/in_patient_status/in_patient_status.json b/erpnext/healthcare/dashboard_chart/in_patient_status/in_patient_status.json
deleted file mode 100644
index 77b47c9..0000000
--- a/erpnext/healthcare/dashboard_chart/in_patient_status/in_patient_status.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "chart_name": "In-Patient Status",
- "chart_type": "Group By",
- "creation": "2020-07-14 18:17:54.629199",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Inpatient Record",
- "dynamic_filters_json": "[[\"Inpatient Record\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[]",
- "group_by_based_on": "status",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:46.792131",
- "modified": "2020-07-22 13:33:16.008150",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "In-Patient Status",
- "number_of_groups": 0,
- "owner": "Administrator",
- "timeseries": 0,
- "type": "Bar",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json b/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json
deleted file mode 100644
index 0524835..0000000
--- a/erpnext/healthcare/dashboard_chart/lab_tests/lab_tests.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "chart_name": "Lab Tests",
- "chart_type": "Group By",
- "creation": "2020-07-14 18:17:54.574903",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Lab Test",
- "dynamic_filters_json": "[[\"Lab Test\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[[\"Lab Test\",\"docstatus\",\"=\",\"1\",false]]",
- "group_by_based_on": "template",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:47.344055",
- "modified": "2020-07-22 13:37:34.490129",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Tests",
- "number_of_groups": 0,
- "owner": "Administrator",
- "timeseries": 0,
- "type": "Percentage",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/patient_appointments/patient_appointments.json b/erpnext/healthcare/dashboard_chart/patient_appointments/patient_appointments.json
deleted file mode 100644
index 19bfb72..0000000
--- a/erpnext/healthcare/dashboard_chart/patient_appointments/patient_appointments.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "based_on": "appointment_datetime",
- "chart_name": "Patient Appointments",
- "chart_type": "Count",
- "creation": "2020-07-14 18:17:54.525082",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Patient Appointment",
- "dynamic_filters_json": "[[\"Patient Appointment\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[[\"Patient Appointment\",\"status\",\"!=\",\"Cancelled\",false]]",
- "idx": 0,
- "is_public": 0,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:46.830491",
- "modified": "2020-07-22 13:38:02.254190",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Appointments",
- "number_of_groups": 0,
- "owner": "Administrator",
- "time_interval": "Daily",
- "timeseries": 1,
- "timespan": "Last Month",
- "type": "Line",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json b/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json
deleted file mode 100644
index 8fc86a1..0000000
--- a/erpnext/healthcare/dashboard_chart/symptoms/symptoms.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "chart_name": "Symptoms",
- "chart_type": "Group By",
- "creation": "2020-07-14 18:17:54.680852",
- "docstatus": 0,
- "doctype": "Dashboard Chart",
- "document_type": "Patient Encounter Symptom",
- "dynamic_filters_json": "",
- "filters_json": "[]",
- "group_by_based_on": "complaint",
- "group_by_type": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "last_synced_on": "2020-07-22 13:22:47.296748",
- "modified": "2020-07-22 13:40:59.655129",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Symptoms",
- "number_of_groups": 0,
- "owner": "Administrator",
- "timeseries": 0,
- "type": "Percentage",
- "use_report_chart": 0,
- "y_axis": []
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart_source/__init__.py b/erpnext/healthcare/dashboard_chart_source/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/dashboard_chart_source/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js
deleted file mode 100644
index dd6dc66..0000000
--- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js
+++ /dev/null
@@ -1,14 +0,0 @@
-frappe.provide('frappe.dashboards.chart_sources');
-
-frappe.dashboards.chart_sources["Department wise Patient Appointments"] = {
- method: "erpnext.healthcare.dashboard_chart_source.department_wise_patient_appointments.department_wise_patient_appointments.get",
- filters: [
- {
- fieldname: "company",
- label: __("Company"),
- fieldtype: "Link",
- options: "Company",
- default: frappe.defaults.get_user_default("Company")
- }
- ]
-};
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json
deleted file mode 100644
index 00301ef..0000000
--- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "creation": "2020-05-18 19:18:42.571045",
- "docstatus": 0,
- "doctype": "Dashboard Chart Source",
- "idx": 0,
- "modified": "2020-05-18 19:18:42.571045",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Department wise Patient Appointments",
- "owner": "Administrator",
- "source_name": "Department wise Patient Appointments",
- "timeseries": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py
deleted file mode 100644
index 062da6e..0000000
--- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.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
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils.dashboard import cache_source
-
-@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):
- if chart_name:
- chart = frappe.get_doc('Dashboard Chart', chart_name)
- else:
- chart = frappe._dict(frappe.parse_json(chart))
-
- filters = frappe.parse_json(filters)
-
- data = frappe.db.get_list('Medical Department', fields=['name'])
- if not filters:
- filters = {}
-
- status = ['Open', 'Scheduled', 'Closed', 'Cancelled']
- for department in data:
- filters['department'] = department.name
- department['total_appointments'] = frappe.db.count('Patient Appointment', filters=filters)
-
- for entry in status:
- filters['status'] = entry
- department[frappe.scrub(entry)] = frappe.db.count('Patient Appointment', filters=filters)
- filters.pop('status')
-
- sorted_department_map = sorted(data, key = lambda i: i['total_appointments'], reverse=True)
-
- if len(sorted_department_map) > 10:
- sorted_department_map = sorted_department_map[:10]
-
- labels = []
- open_appointments = []
- scheduled = []
- closed = []
- cancelled = []
-
- for department in sorted_department_map:
- labels.append(department.name)
- open_appointments.append(department.open)
- scheduled.append(department.scheduled)
- closed.append(department.closed)
- cancelled.append(department.cancelled)
-
- return {
- 'labels': labels,
- 'datasets': [
- {
- 'name': 'Open',
- 'values': open_appointments
- },
- {
- 'name': 'Scheduled',
- 'values': scheduled
- },
- {
- 'name': 'Closed',
- 'values': closed
- },
- {
- 'name': 'Cancelled',
- 'values': cancelled
- }
- ],
- 'type': 'bar'
- }
\ No newline at end of file
diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json
deleted file mode 100644
index af601f3..0000000
--- a/erpnext/healthcare/desk_page/healthcare/healthcare.json
+++ /dev/null
@@ -1,122 +0,0 @@
-{
- "cards": [
- {
- "hidden": 0,
- "label": "Masters",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient\",\n\t\t\"label\": \"Patient\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Practitioner\",\n\t\t\"label\":\"Healthcare Practitioner\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Practitioner Schedule\",\n\t\t\"label\": \"Practitioner Schedule\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Department\",\n\t\t\"label\": \"Medical Department\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit Type\",\n\t\t\"label\": \"Healthcare Service Unit Type\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit\",\n\t\t\"label\": \"Healthcare Service Unit\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code Standard\",\n\t\t\"label\": \"Medical Code Standard\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code\",\n\t\t\"label\": \"Medical Code\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Consultation Setup",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Appointment Type\",\n\t\t\"label\": \"Appointment Type\"\n },\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure Template\",\n\t\t\"label\": \"Clinical Procedure Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Dosage\",\n\t\t\"label\": \"Prescription Dosage\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Duration\",\n\t\t\"label\": \"Prescription Duration\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Antibiotic\",\n\t\t\"label\": \"Antibiotic\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Consultation",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Appointment\",\n\t\t\"label\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure\",\n\t\t\"label\": \"Clinical Procedure\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Encounter\",\n\t\t\"label\": \"Patient Encounter\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Vital Signs\",\n\t\t\"label\": \"Vital Signs\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Complaint\",\n\t\t\"label\": \"Complaint\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Diagnosis\",\n\t\t\"label\": \"Diagnosis\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Fee Validity\",\n\t\t\"label\": \"Fee Validity\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Settings",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Settings\",\n\t\t\"label\": \"Healthcare Settings\",\n\t\t\"onboard\": 1\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Laboratory Setup",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Template\",\n\t\t\"label\": \"Lab Test Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Sample\",\n\t\t\"label\": \"Lab Test Sample\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test UOM\",\n\t\t\"label\": \"Lab Test UOM\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sensitivity\",\n\t\t\"label\": \"Sensitivity\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Laboratory",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Inpatient",
- "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Order\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Entry\",\n\t\t\"label\": \"Inpatient Medication Entry\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Rehabilitation and Physiotherapy",
- "links": "[\n {\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Exercise Type\",\n\t\t\"label\": \"Exercise Type\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Type\",\n\t\t\"label\": \"Therapy Type\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Plan\",\n\t\t\"label\": \"Therapy Plan\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Session\",\n\t\t\"label\": \"Therapy Session\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Assessment Template\",\n\t\t\"label\": \"Patient Assessment Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Assessment\",\n\t\t\"label\": \"Patient Assessment\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Records and History",
- "links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t}\n]"
- },
- {
- "hidden": 0,
- "label": "Reports",
- "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Inpatient Medication Orders\",\n\t\t\"doctype\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Orders\"\n\t}\n]"
- }
- ],
- "category": "Domains",
- "charts": [
- {
- "chart_name": "Patient Appointments",
- "label": "Patient Appointments"
- }
- ],
- "charts_label": "",
- "creation": "2020-03-02 17:23:17.919682",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
- "docstatus": 0,
- "doctype": "Desk Page",
- "extends_another_page": 0,
- "hide_custom": 0,
- "idx": 0,
- "is_standard": 1,
- "label": "Healthcare",
- "modified": "2020-11-26 22:09:09.164584",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare",
- "onboarding": "Healthcare",
- "owner": "Administrator",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
- "restrict_to_domain": "Healthcare",
- "shortcuts": [
- {
- "color": "#ffe8cd",
- "format": "{} Open",
- "label": "Patient Appointment",
- "link_to": "Patient Appointment",
- "stats_filter": "{\n \"status\": \"Open\",\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%']\n}",
- "type": "DocType"
- },
- {
- "color": "#ffe8cd",
- "format": "{} Active",
- "label": "Patient",
- "link_to": "Patient",
- "stats_filter": "{\n \"status\": \"Active\"\n}",
- "type": "DocType"
- },
- {
- "color": "#cef6d1",
- "format": "{} Vacant",
- "label": "Healthcare Service Unit",
- "link_to": "Healthcare Service Unit",
- "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0,\n \"company\": [\"like\", \"%\" + frappe.defaults.get_global_default(\"company\") + \"%\"]\n}",
- "type": "DocType"
- },
- {
- "label": "Healthcare Practitioner",
- "link_to": "Healthcare Practitioner",
- "type": "DocType"
- },
- {
- "label": "Patient History",
- "link_to": "patient_history",
- "type": "Page"
- },
- {
- "label": "Dashboard",
- "link_to": "Healthcare",
- "type": "Dashboard"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/antibiotic/__init__.py b/erpnext/healthcare/doctype/antibiotic/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/antibiotic/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/antibiotic/antibiotic.js b/erpnext/healthcare/doctype/antibiotic/antibiotic.js
deleted file mode 100644
index 42e6adb..0000000
--- a/erpnext/healthcare/doctype/antibiotic/antibiotic.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Antibiotic', {
-});
diff --git a/erpnext/healthcare/doctype/antibiotic/antibiotic.json b/erpnext/healthcare/doctype/antibiotic/antibiotic.json
deleted file mode 100644
index 41a3e31..0000000
--- a/erpnext/healthcare/doctype/antibiotic/antibiotic.json
+++ /dev/null
@@ -1,151 +0,0 @@
-{
- "allow_copy": 1,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:antibiotic_name",
- "beta": 1,
- "creation": "2016-02-23 11:11:30.749731",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "antibiotic_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": "Antibiotic 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,
- "fetch_if_empty": 0,
- "fieldname": "abbr",
- "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": "Abbr",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 1
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-10-01 17:58:23.136498",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Antibiotic",
- "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": "Healthcare Administrator",
- "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": "Laboratory User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "antibiotic_name",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "antibiotic_name",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/antibiotic/antibiotic.py b/erpnext/healthcare/doctype/antibiotic/antibiotic.py
deleted file mode 100644
index 8236c8a..0000000
--- a/erpnext/healthcare/doctype/antibiotic/antibiotic.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class Antibiotic(Document):
- pass
diff --git a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js
deleted file mode 100644
index b92103d..0000000
--- a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Antibiotic", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Antibiotic
- () => frappe.tests.make('Antibiotic', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.py b/erpnext/healthcare/doctype/antibiotic/test_antibiotic.py
deleted file mode 100644
index 6ac4f4f..0000000
--- a/erpnext/healthcare/doctype/antibiotic/test_antibiotic.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestAntibiotic(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/appointment_type/__init__.py b/erpnext/healthcare/doctype/appointment_type/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/appointment_type/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
deleted file mode 100644
index 861675a..0000000
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Appointment Type', {
- refresh: function(frm) {
- frm.set_query('price_list', function() {
- return {
- filters: {'selling': 1}
- };
- });
-
- frm.set_query('medical_department', 'items', function(doc) {
- let item_list = doc.items.map(({medical_department}) => medical_department);
- return {
- filters: [
- ['Medical Department', 'name', 'not in', item_list]
- ]
- };
- });
-
- frm.set_query('op_consulting_charge_item', 'items', function() {
- return {
- filters: {
- is_stock_item: 0
- }
- };
- });
-
- frm.set_query('inpatient_visit_charge_item', 'items', function() {
- return {
- filters: {
- is_stock_item: 0
- }
- };
- });
- }
-});
-
-frappe.ui.form.on('Appointment Type Service Item', {
- op_consulting_charge_item: function(frm, cdt, cdn) {
- let d = locals[cdt][cdn];
- if (frm.doc.price_list && d.op_consulting_charge_item) {
- frappe.call({
- 'method': 'frappe.client.get_value',
- args: {
- 'doctype': 'Item Price',
- 'filters': {
- 'item_code': d.op_consulting_charge_item,
- 'price_list': frm.doc.price_list
- },
- 'fieldname': ['price_list_rate']
- },
- callback: function(data) {
- if (data.message.price_list_rate) {
- frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate);
- }
- }
- });
- }
- },
-
- inpatient_visit_charge_item: function(frm, cdt, cdn) {
- let d = locals[cdt][cdn];
- if (frm.doc.price_list && d.inpatient_visit_charge_item) {
- frappe.call({
- 'method': 'frappe.client.get_value',
- args: {
- 'doctype': 'Item Price',
- 'filters': {
- 'item_code': d.inpatient_visit_charge_item,
- 'price_list': frm.doc.price_list
- },
- 'fieldname': ['price_list_rate']
- },
- callback: function (data) {
- if (data.message.price_list_rate) {
- frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate);
- }
- }
- });
- }
- }
-});
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
deleted file mode 100644
index 3872318..0000000
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json
+++ /dev/null
@@ -1,114 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:appointment_type",
- "beta": 1,
- "creation": "2016-07-22 11:52:34.953019",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
- "appointment_type",
- "ip",
- "default_duration",
- "color",
- "billing_section",
- "price_list",
- "items"
- ],
- "fields": [
- {
- "allow_in_quick_entry": 1,
- "fieldname": "appointment_type",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Type",
- "reqd": 1,
- "translatable": 1,
- "unique": 1
- },
- {
- "bold": 1,
- "default": "0",
- "fieldname": "ip",
- "fieldtype": "Check",
- "label": "Is Inpatient",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "allow_in_quick_entry": 1,
- "bold": 1,
- "fieldname": "default_duration",
- "fieldtype": "Int",
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Default Duration (In Minutes)"
- },
- {
- "allow_in_quick_entry": 1,
- "fieldname": "color",
- "fieldtype": "Color",
- "in_list_view": 1,
- "label": "Color",
- "no_copy": 1,
- "report_hide": 1
- },
- {
- "fieldname": "billing_section",
- "fieldtype": "Section Break",
- "label": "Billing"
- },
- {
- "fieldname": "price_list",
- "fieldtype": "Link",
- "label": "Price List",
- "options": "Price List"
- },
- {
- "fieldname": "items",
- "fieldtype": "Table",
- "label": "Appointment Type Service Items",
- "options": "Appointment Type Service Item"
- }
- ],
- "links": [],
- "modified": "2021-01-22 09:41:05.010524",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Appointment Type",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "search_fields": "appointment_type",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
deleted file mode 100644
index 67a24f3..0000000
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-import frappe
-
-class AppointmentType(Document):
- def validate(self):
- if self.items and self.price_list:
- for item in self.items:
- existing_op_item_price = frappe.db.exists('Item Price', {
- 'item_code': item.op_consulting_charge_item,
- 'price_list': self.price_list
- })
-
- if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge:
- make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge)
-
- existing_ip_item_price = frappe.db.exists('Item Price', {
- 'item_code': item.inpatient_visit_charge_item,
- 'price_list': self.price_list
- })
-
- if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge:
- make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge)
-
-@frappe.whitelist()
-def get_service_item_based_on_department(appointment_type, department):
- item_list = frappe.db.get_value('Appointment Type Service Item',
- filters = {'medical_department': department, 'parent': appointment_type},
- fieldname = ['op_consulting_charge_item',
- 'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
- as_dict = 1
- )
-
- # if department wise items are not set up
- # use the generic items
- if not item_list:
- item_list = frappe.db.get_value('Appointment Type Service Item',
- filters = {'parent': appointment_type},
- fieldname = ['op_consulting_charge_item',
- 'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
- as_dict = 1
- )
-
- return item_list
-
-def make_item_price(price_list, item, item_price):
- frappe.get_doc({
- 'doctype': 'Item Price',
- 'price_list': price_list,
- 'item_code': item,
- 'price_list_rate': item_price
- }).insert(ignore_permissions=True, ignore_mandatory=True)
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type_dashboard.py b/erpnext/healthcare/doctype/appointment_type/appointment_type_dashboard.py
deleted file mode 100644
index 845e446..0000000
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'appointment_type',
- 'transactions': [
- {
- 'label': _('Patient Appointments'),
- 'items': ['Patient Appointment']
- },
- ]
- }
diff --git a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js
deleted file mode 100644
index 93274e5..0000000
--- a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Appointment Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Appointment Type
- () => frappe.tests.make('Appointment Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.py b/erpnext/healthcare/doctype/appointment_type/test_appointment_type.py
deleted file mode 100644
index 04452e4..0000000
--- a/erpnext/healthcare/doctype/appointment_type/test_appointment_type.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-# test_records = frappe.get_test_records('Appointment Type')
-
-class TestAppointmentType(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
deleted file mode 100644
index 5ff68cd..0000000
--- a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
- "actions": [],
- "creation": "2021-01-22 09:34:53.373105",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "medical_department",
- "op_consulting_charge_item",
- "op_consulting_charge",
- "column_break_4",
- "inpatient_visit_charge_item",
- "inpatient_visit_charge"
- ],
- "fields": [
- {
- "fieldname": "medical_department",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Medical Department",
- "options": "Medical Department"
- },
- {
- "fieldname": "op_consulting_charge_item",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Out Patient Consulting Charge Item",
- "options": "Item"
- },
- {
- "fieldname": "op_consulting_charge",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Out Patient Consulting Charge"
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "inpatient_visit_charge_item",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Inpatient Visit Charge Item",
- "options": "Item"
- },
- {
- "fieldname": "inpatient_visit_charge",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Inpatient Visit Charge Item"
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-01-22 09:35:26.503443",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Appointment Type Service Item",
- "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/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
deleted file mode 100644
index b2e0e82..0000000
--- a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class AppointmentTypeServiceItem(Document):
- pass
diff --git a/erpnext/healthcare/doctype/body_part/body_part.js b/erpnext/healthcare/doctype/body_part/body_part.js
deleted file mode 100644
index d2f9d09..0000000
--- a/erpnext/healthcare/doctype/body_part/body_part.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Body Part', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/healthcare/doctype/body_part/body_part.json b/erpnext/healthcare/doctype/body_part/body_part.json
deleted file mode 100644
index 6e3d1d4..0000000
--- a/erpnext/healthcare/doctype/body_part/body_part.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
- "actions": [],
- "autoname": "field:body_part",
- "creation": "2020-04-10 12:21:55.036402",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "body_part"
- ],
- "fields": [
- {
- "fieldname": "body_part",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Body Part",
- "reqd": 1,
- "unique": 1
- }
- ],
- "links": [],
- "modified": "2020-04-10 12:26:44.087985",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Body Part",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/body_part/body_part.py b/erpnext/healthcare/doctype/body_part/body_part.py
deleted file mode 100644
index 300493a..0000000
--- a/erpnext/healthcare/doctype/body_part/body_part.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class BodyPart(Document):
- pass
diff --git a/erpnext/healthcare/doctype/body_part/test_body_part.py b/erpnext/healthcare/doctype/body_part/test_body_part.py
deleted file mode 100644
index cb3a611..0000000
--- a/erpnext/healthcare/doctype/body_part/test_body_part.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestBodyPart(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/body_part_link/__init__.py b/erpnext/healthcare/doctype/body_part_link/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/body_part_link/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/body_part_link/body_part_link.json b/erpnext/healthcare/doctype/body_part_link/body_part_link.json
deleted file mode 100644
index 400b7c6..0000000
--- a/erpnext/healthcare/doctype/body_part_link/body_part_link.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "actions": [],
- "creation": "2020-04-10 12:23:15.259816",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "body_part"
- ],
- "fields": [
- {
- "fieldname": "body_part",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Body Part",
- "options": "Body Part",
- "reqd": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-04-10 12:25:23.101749",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Body Part Link",
- "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/healthcare/doctype/body_part_link/body_part_link.py b/erpnext/healthcare/doctype/body_part_link/body_part_link.py
deleted file mode 100644
index 0371529..0000000
--- a/erpnext/healthcare/doctype/body_part_link/body_part_link.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class BodyPartLink(Document):
- pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure/__init__.py b/erpnext/healthcare/doctype/clinical_procedure/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
deleted file mode 100644
index b55d5d6..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright (c) 2017, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Clinical Procedure', {
- setup: function(frm) {
- frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
- let item = locals[cdt][cdn];
- if (!item.item_code) {
- frappe.throw(__('Please enter Item Code to get Batch Number'));
- } else {
- let filters = {'item_code': item.item_code};
-
- if (frm.doc.status == 'In Progress') {
- filters['posting_date'] = frm.doc.start_date || frappe.datetime.nowdate();
- if (frm.doc.warehouse) filters['warehouse'] = frm.doc.warehouse;
- }
-
- return {
- query : 'erpnext.controllers.queries.get_batch_no',
- filters: filters
- };
- }
- });
- },
-
- refresh: function(frm) {
- frm.set_query('patient', function () {
- return {
- filters: {'status': ['!=', 'Disabled']}
- };
- });
-
- frm.set_query('appointment', function () {
- return {
- filters: {
- 'procedure_template': ['not in', null],
- 'status': ['in', 'Open, Scheduled']
- }
- };
- });
-
- frm.set_query('service_unit', function() {
- return {
- filters: {
- 'is_group': false,
- 'allow_appointments': true,
- 'company': frm.doc.company
- }
- };
- });
-
- frm.set_query('practitioner', function() {
- return {
- filters: {
- 'department': frm.doc.medical_department
- }
- };
- });
-
- if (frm.doc.consume_stock) {
- frm.set_indicator_formatter('item_code',
- function(doc) { return (doc.qty<=doc.actual_qty) ? 'green' : 'orange' ; });
- }
-
- if (frm.doc.docstatus == 1) {
- if (frm.doc.status == 'In Progress') {
- let btn_label = '';
- let msg = '';
- if (frm.doc.consume_stock) {
- btn_label = __('Complete and Consume');
- msg = __('Complete {0} and Consume Stock?', [frm.doc.name]);
- } else {
- btn_label = 'Complete';
- msg = __('Complete {0}?', [frm.doc.name]);
- }
-
- frm.add_custom_button(__(btn_label), function () {
- frappe.confirm(
- msg,
- function() {
- frappe.call({
- method: 'complete_procedure',
- doc: frm.doc,
- freeze: true,
- callback: function(r) {
- if (r.message) {
- frappe.show_alert({
- message: __('Stock Entry {0} created', ['<a class="bold" href="/app/stock-entry/'+ r.message + '">' + r.message + '</a>']),
- indicator: 'green'
- });
- }
- frm.reload_doc();
- }
- });
- }
- );
- }).addClass("btn-primary");
-
- } else if (frm.doc.status == 'Pending') {
- frm.add_custom_button(__('Start'), function() {
- frappe.call({
- doc: frm.doc,
- method: 'start_procedure',
- callback: function(r) {
- if (!r.exc) {
- if (r.message == 'insufficient stock') {
- let msg = __('Stock quantity to start the Procedure is not available in the Warehouse {0}. Do you want to record a Stock Entry?', [frm.doc.warehouse.bold()]);
- frappe.confirm(
- msg,
- function() {
- frappe.call({
- doc: frm.doc,
- method: 'make_material_receipt',
- freeze: true,
- callback: function(r) {
- if (!r.exc) {
- frm.reload_doc();
- let doclist = frappe.model.sync(r.message);
- frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
- }
- }
- });
- }
- );
- } else {
- frm.reload_doc();
- }
- }
- }
- });
- }).addClass("btn-primary");
- }
- }
- },
-
- onload: function(frm) {
- if (frm.is_new()) {
- frm.add_fetch('procedure_template', 'medical_department', 'medical_department');
- frm.set_value('start_time', null);
- }
- },
-
- patient: function(frm) {
- if (frm.doc.patient) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
- args: {
- patient: frm.doc.patient
- },
- callback: function (data) {
- let age = '';
- if (data.message.dob) {
- age = calculate_age(data.message.dob);
- } else if (data.message.age) {
- age = data.message.age;
- if (data.message.age_as_on) {
- age = __('{0} as on {1}', [age, data.message.age_as_on]);
- }
- }
- frm.set_value('patient_name', data.message.patient_name);
- frm.set_value('patient_age', age);
- frm.set_value('patient_sex', data.message.sex);
- }
- });
- } else {
- frm.set_value('patient_name', '');
- frm.set_value('patient_age', '');
- frm.set_value('patient_sex', '');
- }
- },
-
- appointment: function(frm) {
- if (frm.doc.appointment) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Patient Appointment',
- name: frm.doc.appointment
- },
- callback: function(data) {
- let values = {
- 'patient':data.message.patient,
- 'procedure_template': data.message.procedure_template,
- 'medical_department': data.message.department,
- 'practitioner': data.message.practitioner,
- 'start_date': data.message.appointment_date,
- 'start_time': data.message.appointment_time,
- 'notes': data.message.notes,
- 'service_unit': data.message.service_unit,
- 'company': data.message.company
- };
- frm.set_value(values);
- }
- });
- } else {
- let values = {
- 'patient': '',
- 'patient_name': '',
- 'patient_sex': '',
- 'patient_age': '',
- 'medical_department': '',
- 'procedure_template': '',
- 'start_date': '',
- 'start_time': '',
- 'notes': '',
- 'service_unit': '',
- 'inpatient_record': ''
- };
- frm.set_value(values);
- }
- },
-
- procedure_template: function(frm) {
- if (frm.doc.procedure_template) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Clinical Procedure Template',
- name: frm.doc.procedure_template
- },
- callback: function (data) {
- frm.set_value('medical_department', data.message.medical_department);
- frm.set_value('consume_stock', data.message.consume_stock);
- frm.events.set_warehouse(frm);
- frm.events.set_procedure_consumables(frm);
- }
- });
- }
- },
-
- service_unit: function(frm) {
- if (frm.doc.service_unit) {
- frappe.call({
- method: 'frappe.client.get_value',
- args:{
- fieldname: 'warehouse',
- doctype: 'Healthcare Service Unit',
- filters:{name: frm.doc.service_unit},
- },
- callback: function(data) {
- if (data.message) {
- frm.set_value('warehouse', data.message.warehouse);
- }
- }
- });
- }
- },
-
- practitioner: function(frm) {
- if (frm.doc.practitioner) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Healthcare Practitioner',
- name: frm.doc.practitioner
- },
- callback: function (data) {
- frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', data.message.practitioner_name);
- }
- });
- } else {
- frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', '');
- }
- },
-
- set_warehouse: function(frm) {
- if (!frm.doc.warehouse) {
- frappe.call({
- method: 'frappe.client.get_value',
- args: {
- doctype: 'Stock Settings',
- fieldname: 'default_warehouse'
- },
- callback: function (data) {
- frm.set_value('warehouse', data.message.default_warehouse);
- }
- });
- }
- },
-
- set_procedure_consumables: function(frm) {
- frappe.call({
- method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.get_procedure_consumables',
- args: {
- procedure_template: frm.doc.procedure_template
- },
- callback: function(data) {
- if (data.message) {
- frm.doc.items = [];
- $.each(data.message, function(i, v) {
- let item = frm.add_child('items');
- item.item_code = v.item_code;
- item.item_name = v.item_name;
- item.uom = v.uom;
- item.stock_uom = v.stock_uom;
- item.qty = flt(v.qty);
- item.transfer_qty = v.transfer_qty;
- item.conversion_factor = v.conversion_factor;
- item.invoice_separately_as_consumables = v.invoice_separately_as_consumables;
- item.batch_no = v.batch_no;
- });
- refresh_field('items');
- }
- }
- });
- }
-
-});
-
-frappe.ui.form.on('Clinical Procedure Item', {
- qty: function(frm, cdt, cdn) {
- let d = locals[cdt][cdn];
- frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty*d.conversion_factor);
- },
-
- uom: function(doc, cdt, cdn) {
- let d = locals[cdt][cdn];
- if (d.uom && d.item_code) {
- return frappe.call({
- method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details',
- args: {
- item_code: d.item_code,
- uom: d.uom,
- qty: d.qty
- },
- callback: function(r) {
- if (r.message) {
- frappe.model.set_value(cdt, cdn, r.message);
- }
- }
- });
- }
- },
-
- item_code: function(frm, cdt, cdn) {
- let d = locals[cdt][cdn];
- let args = null;
- if (d.item_code) {
- args = {
- 'doctype' : 'Clinical Procedure',
- 'item_code' : d.item_code,
- 'company' : frm.doc.company,
- 'warehouse': frm.doc.warehouse
- };
- return frappe.call({
- method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details',
- args: {args: args},
- callback: function(r) {
- if (r.message) {
- let d = locals[cdt][cdn];
- $.each(r.message, function(k, v) {
- d[k] = v;
- });
- refresh_field('items');
- }
- }
- });
- }
- }
-});
-
-let calculate_age = function(birth) {
- let ageMS = Date.parse(Date()) - Date.parse(birth);
- let age = new Date();
- age.setTime(ageMS);
- let years = age.getFullYear() - 1970;
- return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
-};
-
-// List Stock items
-cur_frm.set_query('item_code', 'items', function() {
- return {
- filters: {
- is_stock_item:1
- }
- };
-});
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json
deleted file mode 100644
index b1d62da..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json
+++ /dev/null
@@ -1,345 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2017-04-07 12:52:43.542429",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "title",
- "appointment",
- "procedure_template",
- "medical_code",
- "column_break_30",
- "company",
- "invoiced",
- "section_break_6",
- "patient",
- "patient_name",
- "patient_sex",
- "patient_age",
- "inpatient_record",
- "notes",
- "column_break_7",
- "status",
- "practitioner",
- "practitioner_name",
- "medical_department",
- "service_unit",
- "start_date",
- "start_time",
- "sample",
- "consumables_section",
- "consume_stock",
- "warehouse",
- "items",
- "section_break_24",
- "invoice_separately_as_consumables",
- "consumption_invoiced",
- "consumable_total_amount",
- "column_break_27",
- "consumption_details",
- "sb_refs",
- "column_break_34",
- "prescription",
- "amended_from"
- ],
- "fields": [
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-CPR-.YYYY.-"
- },
- {
- "fieldname": "appointment",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Appointment",
- "options": "Patient Appointment",
- "set_only_once": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "label": "Age",
- "read_only": 1
- },
- {
- "fieldname": "patient_sex",
- "fieldtype": "Link",
- "label": "Gender",
- "options": "Gender",
- "read_only": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "prescription",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Procedure Prescription",
- "options": "Procedure Prescription",
- "read_only": 1
- },
- {
- "fieldname": "medical_department",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Medical Department",
- "options": "Medical Department"
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "column_break_7",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "procedure_template",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Procedure Template",
- "options": "Clinical Procedure Template",
- "reqd": 1
- },
- {
- "fieldname": "service_unit",
- "fieldtype": "Link",
- "label": "Service Unit",
- "options": "Healthcare Service Unit",
- "set_only_once": 1
- },
- {
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "label": "Warehouse",
- "mandatory_depends_on": "eval: doc.consume_stock == 1",
- "options": "Warehouse"
- },
- {
- "default": "Today",
- "fieldname": "start_date",
- "fieldtype": "Date",
- "label": "Start Date"
- },
- {
- "fieldname": "start_time",
- "fieldtype": "Time",
- "label": "Start Time"
- },
- {
- "fieldname": "sample",
- "fieldtype": "Link",
- "label": "Sample",
- "options": "Sample Collection"
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "notes",
- "fieldtype": "Small Text",
- "label": "Notes",
- "set_only_once": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company"
- },
- {
- "default": "0",
- "fieldname": "consume_stock",
- "fieldtype": "Check",
- "label": "Consume Stock"
- },
- {
- "fieldname": "items",
- "fieldtype": "Table",
- "label": "Consumables",
- "options": "Clinical Procedure Item"
- },
- {
- "default": "0",
- "fieldname": "invoice_separately_as_consumables",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Invoice Consumables Separately",
- "read_only": 1
- },
- {
- "depends_on": "invoice_separately_as_consumables",
- "fieldname": "consumable_total_amount",
- "fieldtype": "Currency",
- "label": "Consumable Total Amount",
- "read_only": 1
- },
- {
- "depends_on": "invoice_separately_as_consumables",
- "fieldname": "consumption_details",
- "fieldtype": "Small Text",
- "label": "Consumption Details"
- },
- {
- "default": "0",
- "depends_on": "invoice_separately_as_consumables",
- "fieldname": "consumption_invoiced",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Consumption Invoiced",
- "read_only": 1
- },
- {
- "depends_on": "eval:!doc.__islocal",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Status",
- "options": "Draft\nSubmitted\nCancelled\nIn Progress\nCompleted\nPending",
- "read_only": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Clinical Procedure",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "consume_stock",
- "fieldname": "consumables_section",
- "fieldtype": "Section Break",
- "label": "Consumables"
- },
- {
- "fieldname": "column_break_27",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_24",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "column_break_30",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_6",
- "fieldtype": "Section Break"
- },
- {
- "collapsible": 1,
- "fieldname": "sb_refs",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fieldname": "practitioner_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Practitioner Name",
- "read_only": 1
- },
- {
- "fieldname": "column_break_34",
- "fieldtype": "Column Break"
- },
- {
- "allow_on_submit": 1,
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fetch_from": "procedure_template.medical_code",
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "label": "Medical Code",
- "options": "Medical Code",
- "read_only": 1
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-06-29 14:28:11.779815",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Clinical Procedure",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
deleted file mode 100644
index cbf89ee..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
+++ /dev/null
@@ -1,252 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import flt, nowdate, nowtime, cstr
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
-from erpnext.healthcare.doctype.lab_test.lab_test import create_sample_doc
-from erpnext.stock.stock_ledger import get_previous_sle
-from erpnext.stock.get_item_details import get_item_details
-from frappe.model.mapper import get_mapped_doc
-
-class ClinicalProcedure(Document):
- def validate(self):
- self.set_status()
- self.set_title()
- if self.consume_stock:
- self.set_actual_qty()
-
- if self.items:
- self.invoice_separately_as_consumables = False
- for item in self.items:
- if item.invoice_separately_as_consumables:
- self.invoice_separately_as_consumables = True
-
- def before_insert(self):
- if self.consume_stock:
- self.set_actual_qty()
-
- def after_insert(self):
- if self.prescription:
- frappe.db.set_value('Procedure Prescription', self.prescription, 'procedure_created', 1)
- if self.appointment:
- frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
- template = frappe.get_doc('Clinical Procedure Template', self.procedure_template)
- if template.sample:
- patient = frappe.get_doc('Patient', self.patient)
- sample_collection = create_sample_doc(template, patient, None, self.company)
- frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name)
- self.reload()
-
- def set_status(self):
- if self.docstatus == 0:
- self.status = 'Draft'
- elif self.docstatus == 1:
- if self.status not in ['In Progress', 'Completed']:
- self.status = 'Pending'
- elif self.docstatus == 2:
- self.status = 'Cancelled'
-
- def set_title(self):
- self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
-
- @frappe.whitelist()
- def complete_procedure(self):
- if self.consume_stock and self.items:
- stock_entry = make_stock_entry(self)
-
- if self.items:
- consumable_total_amount = 0
- consumption_details = False
- customer = frappe.db.get_value('Patient', self.patient, 'customer')
- if customer:
- for item in self.items:
- if item.invoice_separately_as_consumables:
- price_list, price_list_currency = frappe.db.get_values('Price List', {'selling': 1}, ['name', 'currency'])[0]
- args = {
- 'doctype': 'Sales Invoice',
- 'item_code': item.item_code,
- 'company': self.company,
- 'warehouse': self.warehouse,
- 'customer': customer,
- 'selling_price_list': price_list,
- 'price_list_currency': price_list_currency,
- 'plc_conversion_rate': 1.0,
- 'conversion_rate': 1.0
- }
- item_details = get_item_details(args)
- item_price = item_details.price_list_rate * item.qty
- item_consumption_details = item_details.item_name + ' ' + str(item.qty) + ' ' + item.uom + ' ' + str(item_price)
- consumable_total_amount += item_price
- if not consumption_details:
- consumption_details = _('Clinical Procedure ({0}):').format(self.name)
- consumption_details += '\n\t' + item_consumption_details
-
- if consumable_total_amount > 0:
- frappe.db.set_value('Clinical Procedure', self.name, 'consumable_total_amount', consumable_total_amount)
- frappe.db.set_value('Clinical Procedure', self.name, 'consumption_details', consumption_details)
- else:
- frappe.throw(_('Please set Customer in Patient {0}').format(frappe.bold(self.patient)), title=_('Customer Not Found'))
-
- self.db_set('status', 'Completed')
-
- if self.consume_stock and self.items:
- return stock_entry
-
- @frappe.whitelist()
- def start_procedure(self):
- allow_start = self.set_actual_qty()
- if allow_start:
- self.db_set('status', 'In Progress')
- return 'success'
- return 'insufficient stock'
-
- def set_actual_qty(self):
- allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
-
- allow_start = True
- for d in self.get('items'):
- d.actual_qty = get_stock_qty(d.item_code, self.warehouse)
- # validate qty
- if not allow_negative_stock and d.actual_qty < d.qty:
- allow_start = False
- break
-
- return allow_start
-
- @frappe.whitelist()
- def make_material_receipt(self, submit=False):
- stock_entry = frappe.new_doc('Stock Entry')
-
- stock_entry.stock_entry_type = 'Material Receipt'
- stock_entry.to_warehouse = self.warehouse
- stock_entry.company = self.company
- expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
- for item in self.items:
- if item.qty > item.actual_qty:
- se_child = stock_entry.append('items')
- se_child.item_code = item.item_code
- se_child.item_name = item.item_name
- se_child.uom = item.uom
- se_child.stock_uom = item.stock_uom
- se_child.qty = flt(item.qty - item.actual_qty)
- se_child.t_warehouse = self.warehouse
- # in stock uom
- se_child.transfer_qty = flt(item.transfer_qty)
- se_child.conversion_factor = flt(item.conversion_factor)
- cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
- se_child.cost_center = cost_center
- se_child.expense_account = expense_account
- if submit:
- stock_entry.submit()
- return stock_entry
- return stock_entry.as_dict()
-
-
-def get_stock_qty(item_code, warehouse):
- return get_previous_sle({
- 'item_code': item_code,
- 'warehouse': warehouse,
- 'posting_date': nowdate(),
- 'posting_time': nowtime()
- }).get('qty_after_transaction') or 0
-
-
-@frappe.whitelist()
-def get_procedure_consumables(procedure_template):
- return get_items('Clinical Procedure Item', procedure_template, 'Clinical Procedure Template')
-
-
-@frappe.whitelist()
-def set_stock_items(doc, stock_detail_parent, parenttype):
- items = get_items('Clinical Procedure Item', stock_detail_parent, parenttype)
-
- for item in items:
- se_child = doc.append('items')
- se_child.item_code = item.item_code
- se_child.item_name = item.item_name
- se_child.uom = item.uom
- se_child.stock_uom = item.stock_uom
- se_child.qty = flt(item.qty)
- # in stock uom
- se_child.transfer_qty = flt(item.transfer_qty)
- se_child.conversion_factor = flt(item.conversion_factor)
- if item.batch_no:
- se_child.batch_no = item.batch_no
- if parenttype == 'Clinical Procedure Template':
- se_child.invoice_separately_as_consumables = item.invoice_separately_as_consumables
-
- return doc
-
-
-def get_items(table, parent, parenttype):
- items = frappe.db.get_all(table, filters={
- 'parent': parent,
- 'parenttype': parenttype
- }, fields=['*'])
-
- return items
-
-
-@frappe.whitelist()
-def make_stock_entry(doc):
- stock_entry = frappe.new_doc('Stock Entry')
- stock_entry = set_stock_items(stock_entry, doc.name, 'Clinical Procedure')
- stock_entry.stock_entry_type = 'Material Issue'
- stock_entry.from_warehouse = doc.warehouse
- stock_entry.company = doc.company
- expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
-
- for item_line in stock_entry.items:
- cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
- item_line.cost_center = cost_center
- item_line.expense_account = expense_account
-
- stock_entry.save(ignore_permissions=True)
- stock_entry.submit()
- return stock_entry.name
-
-
-@frappe.whitelist()
-def make_procedure(source_name, target_doc=None):
- def set_missing_values(source, target):
- consume_stock = frappe.db.get_value('Clinical Procedure Template', source.procedure_template, 'consume_stock')
- if consume_stock:
- target.consume_stock = 1
- warehouse = None
- if source.service_unit:
- warehouse = frappe.db.get_value('Healthcare Service Unit', source.service_unit, 'warehouse')
- if not warehouse:
- warehouse = frappe.db.get_value('Stock Settings', None, 'default_warehouse')
- if warehouse:
- target.warehouse = warehouse
-
- set_stock_items(target, source.procedure_template, 'Clinical Procedure Template')
-
- doc = get_mapped_doc('Patient Appointment', source_name, {
- 'Patient Appointment': {
- 'doctype': 'Clinical Procedure',
- 'field_map': [
- ['appointment', 'name'],
- ['patient', 'patient'],
- ['patient_age', 'patient_age'],
- ['patient_sex', 'patient_sex'],
- ['procedure_template', 'procedure_template'],
- ['prescription', 'procedure_prescription'],
- ['practitioner', 'practitioner'],
- ['medical_department', 'department'],
- ['start_date', 'appointment_date'],
- ['start_time', 'appointment_time'],
- ['notes', 'notes'],
- ['service_unit', 'service_unit'],
- ['company', 'company'],
- ['invoiced', 'invoiced']
- ]
- }
- }, target_doc, set_missing_values)
-
- return doc
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure_list.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure_list.js
deleted file mode 100644
index c8601f9..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure_list.js
+++ /dev/null
@@ -1,11 +0,0 @@
-frappe.listview_settings['Clinical Procedure'] = {
- get_indicator: function(doc) {
- var colors = {
- 'Completed': 'green',
- 'In Progress': 'orange',
- 'Pending': 'orange',
- 'Cancelled': 'grey'
- };
- return [__(doc.status), colors[doc.status], 'status,=,' + doc.status];
- }
-};
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.js
deleted file mode 100644
index 80ef3d5..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Clinical Procedure", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Clinical Procedure
- () => frappe.tests.make('Clinical Procedure', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
deleted file mode 100644
index 03e96a4..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
+++ /dev/null
@@ -1,66 +0,0 @@
- # -*- coding: utf-8 -*-
-# Copyright (c) 2017, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-import frappe
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_clinical_procedure_template
-
-test_dependencies = ['Item']
-
-class TestClinicalProcedure(unittest.TestCase):
- def test_procedure_template_item(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- procedure_template = create_clinical_procedure_template()
- self.assertTrue(frappe.db.exists('Item', procedure_template.item))
-
- procedure_template.disabled = 1
- procedure_template.save()
- self.assertEqual(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
-
- def test_consumables(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- procedure_template = create_clinical_procedure_template()
- procedure_template.allow_stock_consumption = 1
- consumable = create_consumable()
- procedure_template.append('items', {
- 'item_code': consumable.item_code,
- 'qty': 1,
- 'uom': consumable.stock_uom,
- 'stock_uom': consumable.stock_uom
- })
- procedure_template.save()
- procedure = create_procedure(procedure_template, patient, practitioner)
- result = procedure.start_procedure()
- if result == 'insufficient stock':
- procedure.make_material_receipt(submit=True)
- result = procedure.start_procedure()
- self.assertEqual(procedure.status, 'In Progress')
- result = procedure.complete_procedure()
- # check consumption
- self.assertTrue(frappe.db.exists('Stock Entry', result))
-
-
-def create_consumable():
- if frappe.db.exists('Item', 'Syringe'):
- return frappe.get_doc('Item', 'Syringe')
- consumable = frappe.new_doc('Item')
- consumable.item_code = 'Syringe'
- consumable.item_group = '_Test Item Group'
- consumable.stock_uom = 'Nos'
- consumable.valuation_rate = 5.00
- consumable.save()
- return consumable
-
-def create_procedure(procedure_template, patient, practitioner):
- procedure = frappe.new_doc('Clinical Procedure')
- procedure.procedure_template = procedure_template.name
- procedure.patient = patient
- procedure.practitioner = practitioner
- procedure.consume_stock = procedure_template.allow_stock_consumption
- procedure.items = procedure_template.items
- procedure.company = "_Test Company"
- procedure.warehouse = "_Test Warehouse - _TC"
- procedure.submit()
- return procedure
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/clinical_procedure_item/__init__.py b/erpnext/healthcare/doctype/clinical_procedure_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_item/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json
deleted file mode 100644
index a7dde0b..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.json
+++ /dev/null
@@ -1,123 +0,0 @@
-{
- "actions": [],
- "beta": 1,
- "creation": "2017-10-05 16:15:10.876952",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "item_code",
- "item_name",
- "qty",
- "barcode",
- "uom",
- "invoice_separately_as_consumables",
- "column_break_5",
- "batch_no",
- "conversion_factor",
- "stock_uom",
- "transfer_qty",
- "actual_qty"
- ],
- "fields": [
- {
- "bold": 1,
- "columns": 3,
- "fieldname": "item_code",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_global_search": 1,
- "in_list_view": 1,
- "label": "Item",
- "options": "Item",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "barcode",
- "fieldtype": "Data",
- "label": "Barcode"
- },
- {
- "fieldname": "item_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Item Name",
- "read_only": 1
- },
- {
- "fieldname": "qty",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Quantity",
- "reqd": 1
- },
- {
- "fieldname": "uom",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "UOM",
- "options": "UOM",
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "invoice_separately_as_consumables",
- "fieldtype": "Check",
- "in_list_view": 1,
- "label": "Invoice Separately as Consumables"
- },
- {
- "fieldname": "column_break_5",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "batch_no",
- "fieldtype": "Link",
- "label": "Batch",
- "options": "Batch"
- },
- {
- "fieldname": "conversion_factor",
- "fieldtype": "Float",
- "label": "Conversion Factor",
- "read_only": 1
- },
- {
- "fieldname": "stock_uom",
- "fieldtype": "Link",
- "label": "Stock UOM",
- "options": "UOM",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "transfer_qty",
- "fieldtype": "Float",
- "label": "Transfer Qty",
- "read_only": 1
- },
- {
- "fieldname": "actual_qty",
- "fieldtype": "Float",
- "label": "Actual Qty (at source/target)",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "search_index": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-03-01 15:34:54.226722",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Clinical Procedure Item",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.py b/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.py
deleted file mode 100644
index d59e517..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_item/clinical_procedure_item.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, earthians and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class ClinicalProcedureItem(Document):
- pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/__init__.py b/erpnext/healthcare/doctype/clinical_procedure_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js
deleted file mode 100644
index 1ef110d..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright (c) 2017, earthians and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Clinical Procedure Template', {
- template: function(frm) {
- if (!frm.doc.item_code)
- frm.set_value('item_code', frm.doc.template);
- if (!frm.doc.description)
- frm.set_value('description', frm.doc.template);
- mark_change_in_item(frm);
- },
-
- rate: function(frm) {
- mark_change_in_item(frm);
- },
-
- is_billable: function (frm) {
- mark_change_in_item(frm);
- },
-
- item_group: function(frm) {
- mark_change_in_item(frm);
- },
-
- description: function(frm) {
- mark_change_in_item(frm);
- },
-
- medical_department: function(frm) {
- mark_change_in_item(frm);
- },
-
- medical_code: function(frm) {
- frm.set_query("medical_code", function() {
- return {
- filters: {
- medical_code_standard: frm.doc.medical_code_standard
- }
- };
- });
- },
-
- refresh: function(frm) {
- frm.fields_dict['items'].grid.set_column_disp('barcode', false);
- frm.fields_dict['items'].grid.set_column_disp('batch_no', false);
-
- if (!frm.doc.__islocal) {
- cur_frm.add_custom_button(__('Change Item Code'), function() {
- change_template_code(frm.doc);
- });
- }
- }
-});
-
-let mark_change_in_item = function(frm) {
- if (!frm.doc.__islocal) {
- frm.doc.change_in_item = 1;
- }
-};
-
-let change_template_code = function(doc) {
- let d = new frappe.ui.Dialog({
- title:__('Change Item Code'),
- fields:[
- {
- 'fieldtype': 'Data',
- 'label': 'Item Code',
- 'fieldname': 'item_code',
- reqd: 1
- }
- ],
- primary_action: function() {
- let values = d.get_values();
-
- if (values) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.change_item_code_from_template',
- 'args': {item_code: values.item_code, doc: doc},
- callback: function () {
- cur_frm.reload_doc();
- frappe.show_alert({
- message: 'Item Code renamed successfully',
- indicator: 'green'
- });
- }
- });
- }
- d.hide();
- },
- primary_action_label: __('Change Item Code')
- });
- d.show();
-
- d.set_values({
- 'item_code': doc.item_code
- });
-};
-
-frappe.ui.form.on('Clinical Procedure Item', {
- qty: function(frm, cdt, cdn) {
- let d = locals[cdt][cdn];
- frappe.model.set_value(cdt, cdn, 'transfer_qty', d.qty * d.conversion_factor);
- },
-
- uom: function(doc, cdt, cdn){
- let d = locals[cdt][cdn];
- if (d.uom && d.item_code) {
- return frappe.call({
- method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_uom_details',
- args: {
- item_code: d.item_code,
- uom: d.uom,
- qty: d.qty
- },
- callback: function(r) {
- if (r.message) {
- frappe.model.set_value(cdt, cdn, r.message);
- }
- }
- });
- }
- },
-
- item_code: function(frm, cdt, cdn) {
- let d = locals[cdt][cdn];
- if (d.item_code) {
- let args = {
- 'item_code' : d.item_code,
- 'transfer_qty' : d.transfer_qty,
- 'quantity' : d.qty
- };
- return frappe.call({
- method: 'erpnext.healthcare.doctype.clinical_procedure_template.clinical_procedure_template.get_item_details',
- args: {args: args},
- callback: function(r) {
- if (r.message) {
- let d = locals[cdt][cdn];
- $.each(r.message, function(k, v) {
- d[k] = v;
- });
- refresh_field('items');
- }
- }
- });
- }
- }
-});
-
-// List Stock items
-cur_frm.set_query('item_code', 'items', function() {
- return {
- filters: {
- is_stock_item:1
- }
- };
-});
-
-frappe.tour['Clinical Procedure Template'] = [
- {
- fieldname: 'template',
- title: __('Template Name'),
- description: __('Enter a name for the Clinical Procedure Template')
- },
- {
- fieldname: 'item_code',
- title: __('Item Code'),
- description: __('Set the Item Code which will be used for billing the Clinical Procedure.')
- },
- {
- fieldname: 'item_group',
- title: __('Item Group'),
- description: __('Select an Item Group for the Clinical Procedure Item.')
- },
- {
- fieldname: 'is_billable',
- title: __('Clinical Procedure Rate'),
- description: __('Check this if the Clinical Procedure is billable and also set the rate.')
- },
- {
- fieldname: 'consume_stock',
- title: __('Allow Stock Consumption'),
- description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "<a href='https://docs.erpnext.com/docs/user/manual/en/healthcare/clinical_procedure_template#22-manage-procedure-consumables' target='_blank'>here</a>" + __(' to know more')
-
- },
- {
- fieldname: 'medical_department',
- title: __('Medical Department'),
- description: __('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.')
- }
-];
-
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
deleted file mode 100644
index 17ac7eb..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
+++ /dev/null
@@ -1,257 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:template",
- "beta": 1,
- "creation": "2017-10-05 14:59:55.438359",
- "description": "Procedure Template",
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "template",
- "item",
- "item_code",
- "item_group",
- "description",
- "column_break_5",
- "disabled",
- "is_billable",
- "rate",
- "medical_department",
- "medical_coding_section",
- "medical_code_standard",
- "medical_code",
- "consumables",
- "consume_stock",
- "items",
- "sample_collection",
- "sample",
- "sample_uom",
- "sample_qty",
- "column_break_21",
- "sample_details",
- "change_in_item"
- ],
- "fields": [
- {
- "fieldname": "template",
- "fieldtype": "Data",
- "in_global_search": 1,
- "in_list_view": 1,
- "label": "Template Name",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "item_code",
- "fieldtype": "Data",
- "label": "Item Code",
- "read_only_depends_on": "eval: !doc.__islocal ",
- "reqd": 1
- },
- {
- "fieldname": "item_group",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Item Group",
- "options": "Item Group",
- "reqd": 1
- },
- {
- "fieldname": "medical_department",
- "fieldtype": "Link",
- "label": "Medical Department",
- "options": "Medical Department"
- },
- {
- "fieldname": "column_break_5",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "is_billable",
- "fieldtype": "Check",
- "label": "Is Billable"
- },
- {
- "depends_on": "is_billable",
- "fieldname": "rate",
- "fieldtype": "Float",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Rate",
- "mandatory_depends_on": "is_billable"
- },
- {
- "fieldname": "description",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Description",
- "no_copy": 1,
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "consume_stock",
- "fieldtype": "Check",
- "label": "Allow Stock Consumption",
- "search_index": 1
- },
- {
- "fieldname": "consumables",
- "fieldtype": "Section Break",
- "label": "Consumables"
- },
- {
- "depends_on": "eval:doc.consume_stock == 1",
- "fieldname": "items",
- "fieldtype": "Table",
- "ignore_user_permissions": 1,
- "label": "Items",
- "options": "Clinical Procedure Item"
- },
- {
- "collapsible": 1,
- "fieldname": "sample_collection",
- "fieldtype": "Section Break",
- "label": "Sample Collection"
- },
- {
- "fieldname": "sample",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Sample",
- "options": "Lab Test Sample"
- },
- {
- "fetch_from": "sample.sample_uom",
- "fieldname": "sample_uom",
- "fieldtype": "Data",
- "label": "Sample UOM",
- "read_only": 1
- },
- {
- "fieldname": "sample_qty",
- "fieldtype": "Float",
- "label": "Quantity"
- },
- {
- "fieldname": "column_break_21",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "sample_details",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Collection Details"
- },
- {
- "default": "0",
- "fieldname": "change_in_item",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Change In Item",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "disabled",
- "fieldtype": "Check",
- "label": "Disabled"
- },
- {
- "fieldname": "item",
- "fieldtype": "Link",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Item",
- "no_copy": 1,
- "options": "Item",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "medical_coding_section",
- "fieldtype": "Section Break",
- "label": "Medical Coding"
- },
- {
- "fieldname": "medical_code_standard",
- "fieldtype": "Link",
- "label": "Medical Code Standard",
- "options": "Medical Code Standard"
- },
- {
- "depends_on": "medical_code_standard",
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "label": "Medical Code",
- "options": "Medical Code"
- }
- ],
- "links": [],
- "modified": "2020-06-29 14:12:27.158130",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Clinical Procedure Template",
- "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": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "template",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "template",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py
deleted file mode 100644
index f32b7cf..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, earthians and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe, json
-from frappe import _
-from frappe.model.document import Document
-from frappe.model.rename_doc import rename_doc
-
-class ClinicalProcedureTemplate(Document):
- def validate(self):
- self.enable_disable_item()
-
- def after_insert(self):
- create_item_from_template(self)
-
- def on_update(self):
- if self.change_in_item:
- self.update_item_and_item_price()
-
- def enable_disable_item(self):
- if self.is_billable:
- if self.disabled:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
- else:
- frappe.db.set_value('Item', self.item, 'disabled', 0)
-
- def update_item_and_item_price(self):
- if self.is_billable and self.item:
- item_doc = frappe.get_doc('Item', {'item_code': self.item})
- item_doc.item_name = self.template
- item_doc.item_group = self.item_group
- item_doc.description = self.description
- item_doc.disabled = 0
- item_doc.save(ignore_permissions=True)
-
- if self.rate:
- item_price = frappe.get_doc('Item Price', {'item_code': self.item})
- item_price.item_name = self.template
- item_price.price_list_rate = self.rate
- item_price.save()
-
- elif not self.is_billable and self.item:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
-
- self.db_set('change_in_item', 0)
-
-
-@frappe.whitelist()
-def get_item_details(args=None):
- if not isinstance(args, dict):
- args = json.loads(args)
-
- item = frappe.db.get_all('Item',
- filters={
- 'disabled': 0,
- 'name': args.get('item_code')
- },
- fields=['stock_uom', 'item_name']
- )
-
- if not item:
- frappe.throw(_('Item {0} is not active').format(args.get('item_code')))
-
- item = item[0]
- ret = {
- 'uom': item.stock_uom,
- 'stock_uom': item.stock_uom,
- 'item_name': item.item_name,
- 'qty': 1,
- 'transfer_qty': 0,
- 'conversion_factor': 1
- }
- return ret
-
-def create_item_from_template(doc):
- disabled = doc.disabled
- if doc.is_billable and not doc.disabled:
- disabled = 0
-
- uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
- item = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': doc.template,
- 'item_name':doc.template,
- 'item_group': doc.item_group,
- 'description':doc.description,
- 'is_sales_item': 1,
- 'is_service_item': 1,
- 'is_purchase_item': 0,
- 'is_stock_item': 0,
- 'show_in_website': 0,
- 'is_pro_applicable': 0,
- 'disabled': disabled,
- 'stock_uom': uom
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- make_item_price(item.name, doc.rate)
- doc.db_set('item', item.name)
-
-def make_item_price(item, item_price):
- price_list_name = frappe.db.get_value('Price List', {'selling': 1})
- 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.whitelist()
-def change_item_code_from_template(item_code, doc):
- doc = frappe._dict(json.loads(doc))
-
- if frappe.db.exists('Item', {'item_code': item_code}):
- frappe.throw(_('Item with Item Code {0} already exists').format(item_code))
- else:
- rename_doc('Item', doc.item_code, item_code, ignore_permissions=True)
- frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code)
- return
-
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template_dashboard.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template_dashboard.py
deleted file mode 100644
index 9aab521..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'procedure_template',
- 'transactions': [
- {
- 'label': _('Consultations'),
- 'items': ['Clinical Procedure']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.js
deleted file mode 100644
index 1dde8b5..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Clinical Procedure Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Clinical Procedure Template
- () => frappe.tests.make('Clinical Procedure Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.py
deleted file mode 100644
index 62e138b..0000000
--- a/erpnext/healthcare/doctype/clinical_procedure_template/test_clinical_procedure_template.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, earthians and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestClinicalProcedureTemplate(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/codification_table/__init__.py b/erpnext/healthcare/doctype/codification_table/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/codification_table/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/codification_table/codification_table.json b/erpnext/healthcare/doctype/codification_table/codification_table.json
deleted file mode 100644
index 9a917b4..0000000
--- a/erpnext/healthcare/doctype/codification_table/codification_table.json
+++ /dev/null
@@ -1,56 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2017-06-22 13:09:23.159579",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "medical_code",
- "code",
- "description"
- ],
- "fields": [
- {
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Medical Code",
- "options": "Medical Code",
- "reqd": 1
- },
- {
- "fetch_from": "medical_code.code",
- "fieldname": "code",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Code",
- "read_only": 1
- },
- {
- "fetch_from": "medical_code.description",
- "fieldname": "description",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Description",
- "read_only": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-02-26 13:17:49.016293",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Codification Table",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/codification_table/codification_table.py b/erpnext/healthcare/doctype/codification_table/codification_table.py
deleted file mode 100644
index ae29c03..0000000
--- a/erpnext/healthcare/doctype/codification_table/codification_table.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class CodificationTable(Document):
- pass
diff --git a/erpnext/healthcare/doctype/complaint/__init__.py b/erpnext/healthcare/doctype/complaint/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/complaint/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/complaint/complaint.js b/erpnext/healthcare/doctype/complaint/complaint.js
deleted file mode 100644
index 5a2d219..0000000
--- a/erpnext/healthcare/doctype/complaint/complaint.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Complaint', {
-});
diff --git a/erpnext/healthcare/doctype/complaint/complaint.json b/erpnext/healthcare/doctype/complaint/complaint.json
deleted file mode 100644
index f600838..0000000
--- a/erpnext/healthcare/doctype/complaint/complaint.json
+++ /dev/null
@@ -1,116 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:complaints",
- "beta": 1,
- "creation": "2017-02-15 12:25:28.045267",
- "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": "complaints",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Complaints",
- "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-10-05 11:18:42.017864",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Complaint",
- "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": "Healthcare Administrator",
- "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": "Physician",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "complaints",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "complaints",
- "track_changes": 0,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/complaint/complaint.py b/erpnext/healthcare/doctype/complaint/complaint.py
deleted file mode 100644
index 717f9db..0000000
--- a/erpnext/healthcare/doctype/complaint/complaint.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class Complaint(Document):
- pass
diff --git a/erpnext/healthcare/doctype/complaint/test_complaint.js b/erpnext/healthcare/doctype/complaint/test_complaint.js
deleted file mode 100644
index 9ff44d8..0000000
--- a/erpnext/healthcare/doctype/complaint/test_complaint.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Complaint", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Complaint
- () => frappe.tests.make('Complaint', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/complaint/test_complaint.py b/erpnext/healthcare/doctype/complaint/test_complaint.py
deleted file mode 100644
index 2b9273a..0000000
--- a/erpnext/healthcare/doctype/complaint/test_complaint.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestComplaint(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/descriptive_test_result/__init__.py b/erpnext/healthcare/doctype/descriptive_test_result/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/descriptive_test_result/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/descriptive_test_result/descriptive_test_result.json b/erpnext/healthcare/doctype/descriptive_test_result/descriptive_test_result.json
deleted file mode 100644
index fcd3828..0000000
--- a/erpnext/healthcare/doctype/descriptive_test_result/descriptive_test_result.json
+++ /dev/null
@@ -1,74 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-02-22 15:12:36.202380",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "lab_test_particulars",
- "result_value",
- "allow_blank",
- "template",
- "require_result_value"
- ],
- "fields": [
- {
- "fieldname": "lab_test_particulars",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Particulars",
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.require_result_value == 1",
- "fieldname": "result_value",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Value"
- },
- {
- "fieldname": "template",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Template",
- "options": "Lab Test Template",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "require_result_value",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Require Result Value",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "default": "1",
- "fieldname": "allow_blank",
- "fieldtype": "Check",
- "label": "Allow Blank",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-07-23 12:33:47.693065",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Descriptive Test Result",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/descriptive_test_result/descriptive_test_result.py b/erpnext/healthcare/doctype/descriptive_test_result/descriptive_test_result.py
deleted file mode 100644
index 7ccf6b5..0000000
--- a/erpnext/healthcare/doctype/descriptive_test_result/descriptive_test_result.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class DescriptiveTestResult(Document):
- pass
diff --git a/erpnext/healthcare/doctype/descriptive_test_template/__init__.py b/erpnext/healthcare/doctype/descriptive_test_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/descriptive_test_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/descriptive_test_template/descriptive_test_template.json b/erpnext/healthcare/doctype/descriptive_test_template/descriptive_test_template.json
deleted file mode 100644
index 9ee8f4f..0000000
--- a/erpnext/healthcare/doctype/descriptive_test_template/descriptive_test_template.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-02-22 16:12:12.394200",
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "particulars",
- "allow_blank"
- ],
- "fields": [
- {
- "fieldname": "particulars",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Result Component"
- },
- {
- "default": "0",
- "fieldname": "allow_blank",
- "fieldtype": "Check",
- "in_list_view": 1,
- "label": "Allow Blank"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-06-24 14:03:51.728863",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Descriptive Test Template",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/descriptive_test_template/descriptive_test_template.py b/erpnext/healthcare/doctype/descriptive_test_template/descriptive_test_template.py
deleted file mode 100644
index 281f32d..0000000
--- a/erpnext/healthcare/doctype/descriptive_test_template/descriptive_test_template.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class DescriptiveTestTemplate(Document):
- pass
diff --git a/erpnext/healthcare/doctype/diagnosis/__init__.py b/erpnext/healthcare/doctype/diagnosis/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/diagnosis/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/diagnosis/diagnosis.js b/erpnext/healthcare/doctype/diagnosis/diagnosis.js
deleted file mode 100644
index fb2557f..0000000
--- a/erpnext/healthcare/doctype/diagnosis/diagnosis.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Diagnosis', {
-});
diff --git a/erpnext/healthcare/doctype/diagnosis/diagnosis.json b/erpnext/healthcare/doctype/diagnosis/diagnosis.json
deleted file mode 100644
index 936c2c5..0000000
--- a/erpnext/healthcare/doctype/diagnosis/diagnosis.json
+++ /dev/null
@@ -1,116 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:diagnosis",
- "beta": 1,
- "creation": "2017-02-15 12:23:59.341108",
- "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": "diagnosis",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Diagnosis",
- "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-10-05 11:25:46.107435",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Diagnosis",
- "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": "Healthcare Administrator",
- "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": "Physician",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "diagnosis",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "diagnosis",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/diagnosis/diagnosis.py b/erpnext/healthcare/doctype/diagnosis/diagnosis.py
deleted file mode 100644
index f56e790..0000000
--- a/erpnext/healthcare/doctype/diagnosis/diagnosis.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class Diagnosis(Document):
- pass
diff --git a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js
deleted file mode 100644
index cacfef5..0000000
--- a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Diagnosis", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Diagnosis
- () => frappe.tests.make('Diagnosis', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.py b/erpnext/healthcare/doctype/diagnosis/test_diagnosis.py
deleted file mode 100644
index c79164d..0000000
--- a/erpnext/healthcare/doctype/diagnosis/test_diagnosis.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-# test_records = frappe.get_test_records('Diagnosis')
-
-class TestDiagnosis(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/dosage_form/__init__.py b/erpnext/healthcare/doctype/dosage_form/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/dosage_form/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/dosage_form/dosage_form.js b/erpnext/healthcare/doctype/dosage_form/dosage_form.js
deleted file mode 100644
index 60e9696..0000000
--- a/erpnext/healthcare/doctype/dosage_form/dosage_form.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Dosage Form', {
-});
diff --git a/erpnext/healthcare/doctype/dosage_form/dosage_form.json b/erpnext/healthcare/doctype/dosage_form/dosage_form.json
deleted file mode 100644
index 350aaed..0000000
--- a/erpnext/healthcare/doctype/dosage_form/dosage_form.json
+++ /dev/null
@@ -1,114 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:dosage_form",
- "beta": 1,
- "creation": "2017-04-08 12:04:33.987972",
- "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": "dosage_form",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Dosage Form",
- "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-10-05 11:24:57.888091",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Dosage Form",
- "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": "Physician",
- "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": "Healthcare Administrator",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "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/healthcare/doctype/dosage_form/dosage_form.py b/erpnext/healthcare/doctype/dosage_form/dosage_form.py
deleted file mode 100644
index 046af08..0000000
--- a/erpnext/healthcare/doctype/dosage_form/dosage_form.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class DosageForm(Document):
- pass
diff --git a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js
deleted file mode 100644
index ba54ab1..0000000
--- a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Dosage Form", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Dosage Form
- () => frappe.tests.make('Dosage Form', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.py b/erpnext/healthcare/doctype/dosage_form/test_dosage_form.py
deleted file mode 100644
index 81cfcf6..0000000
--- a/erpnext/healthcare/doctype/dosage_form/test_dosage_form.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-class TestDosageForm(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/dosage_strength/__init__.py b/erpnext/healthcare/doctype/dosage_strength/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/dosage_strength/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/dosage_strength/dosage_strength.json b/erpnext/healthcare/doctype/dosage_strength/dosage_strength.json
deleted file mode 100644
index da4f1a7..0000000
--- a/erpnext/healthcare/doctype/dosage_strength/dosage_strength.json
+++ /dev/null
@@ -1,102 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2017-02-14 15:40:14.385707",
- "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": "strength",
- "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": "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": 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": "strength_time",
- "fieldtype": "Time",
- "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": "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": 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-08-31 14:11:59.874645",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Dosage Strength",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "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/healthcare/doctype/dosage_strength/dosage_strength.py b/erpnext/healthcare/doctype/dosage_strength/dosage_strength.py
deleted file mode 100644
index e36a016..0000000
--- a/erpnext/healthcare/doctype/dosage_strength/dosage_strength.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class DosageStrength(Document):
- pass
diff --git a/erpnext/healthcare/doctype/drug_prescription/__init__.py b/erpnext/healthcare/doctype/drug_prescription/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/drug_prescription/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json
deleted file mode 100644
index d91e6bf..0000000
--- a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.json
+++ /dev/null
@@ -1,121 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-09-16 16:41:45.533374",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "drug_code",
- "drug_name",
- "dosage",
- "period",
- "dosage_form",
- "column_break_7",
- "comment",
- "usage_interval",
- "interval",
- "interval_uom",
- "update_schedule"
- ],
- "fields": [
- {
- "fieldname": "drug_code",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Drug",
- "options": "Item",
- "reqd": 1
- },
- {
- "fetch_from": "drug_code.item_name",
- "fieldname": "drug_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Drug Name / Description"
- },
- {
- "fieldname": "dosage",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Dosage",
- "options": "Prescription Dosage",
- "reqd": 1
- },
- {
- "fieldname": "period",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Period",
- "options": "Prescription Duration",
- "reqd": 1
- },
- {
- "fieldname": "dosage_form",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Dosage Form",
- "options": "Dosage Form",
- "reqd": 1
- },
- {
- "fieldname": "column_break_7",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "comment",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Comment"
- },
- {
- "depends_on": "usage_interval",
- "fieldname": "interval",
- "fieldtype": "Int",
- "in_list_view": 1,
- "label": "Interval"
- },
- {
- "default": "1",
- "depends_on": "usage_interval",
- "fieldname": "update_schedule",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Update Schedule",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "depends_on": "use_interval",
- "fieldname": "interval_uom",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Interval UOM",
- "options": "\nHour\nDay"
- },
- {
- "default": "0",
- "fieldname": "usage_interval",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Dosage by Time Interval"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-09-30 23:32:09.495288",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Drug Prescription",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py b/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py
deleted file mode 100755
index 68a2dc5..0000000
--- a/erpnext/healthcare/doctype/drug_prescription/drug_prescription.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class DrugPrescription(Document):
- def get_quantity(self):
- quantity = 0
- dosage = None
- period = None
-
- if self.dosage:
- dosage = frappe.get_doc('Prescription Dosage', self.dosage)
- for item in dosage.dosage_strength:
- quantity += item.strength
- if self.period and self.interval:
- period = frappe.get_doc('Prescription Duration', self.period)
- if self.interval < period.get_days():
- quantity = quantity * (period.get_days()/self.interval)
-
- elif self.interval and self.interval_uom and self.period:
- period = frappe.get_doc('Prescription Duration', self.period)
- interval_in = self.interval_uom
- if interval_in == 'Day' and self.interval < period.get_days():
- quantity = period.get_days()/self.interval
- elif interval_in == 'Hour' and self.interval < period.get_hours():
- quantity = period.get_hours()/self.interval
- if quantity > 0:
- return quantity
- else:
- return 1
diff --git a/erpnext/healthcare/doctype/exercise/__init__.py b/erpnext/healthcare/doctype/exercise/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/exercise/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/exercise/exercise.json b/erpnext/healthcare/doctype/exercise/exercise.json
deleted file mode 100644
index 683cc6d..0000000
--- a/erpnext/healthcare/doctype/exercise/exercise.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- "actions": [],
- "creation": "2020-03-11 09:25:00.968572",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "exercise_type",
- "difficulty_level",
- "counts_target",
- "counts_completed",
- "assistance_level"
- ],
- "fields": [
- {
- "fieldname": "exercise_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Exercise Type",
- "options": "Exercise Type",
- "reqd": 1
- },
- {
- "fetch_from": "exercise_type.difficulty_level",
- "fieldname": "difficulty_level",
- "fieldtype": "Link",
- "label": "Difficulty Level",
- "options": "Exercise Difficulty Level"
- },
- {
- "fieldname": "counts_target",
- "fieldtype": "Int",
- "in_list_view": 1,
- "label": "Counts Target"
- },
- {
- "depends_on": "eval:doc.parenttype==\"Therapy\";",
- "fieldname": "counts_completed",
- "fieldtype": "Int",
- "label": "Counts Completed",
- "no_copy": 1
- },
- {
- "fieldname": "assistance_level",
- "fieldtype": "Select",
- "label": "Assistance Level",
- "options": "\nPassive\nActive Assist\nActive"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-11-04 18:20:25.583491",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Exercise",
- "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/healthcare/doctype/exercise/exercise.py b/erpnext/healthcare/doctype/exercise/exercise.py
deleted file mode 100644
index efd8999..0000000
--- a/erpnext/healthcare/doctype/exercise/exercise.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class Exercise(Document):
- pass
diff --git a/erpnext/healthcare/doctype/exercise_difficulty_level/__init__.py b/erpnext/healthcare/doctype/exercise_difficulty_level/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/exercise_difficulty_level/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.js b/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.js
deleted file mode 100644
index ff51c34..0000000
--- a/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Exercise Difficulty Level', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.json b/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.json
deleted file mode 100644
index a6aed75..0000000
--- a/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
- "actions": [],
- "autoname": "field:difficulty_level",
- "creation": "2020-03-29 21:12:55.835941",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "difficulty_level"
- ],
- "fields": [
- {
- "fieldname": "difficulty_level",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Difficulty Level",
- "reqd": 1,
- "unique": 1
- }
- ],
- "links": [],
- "modified": "2020-03-31 23:14:33.554066",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Exercise Difficulty Level",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.py b/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.py
deleted file mode 100644
index 17e97b8..0000000
--- a/erpnext/healthcare/doctype/exercise_difficulty_level/exercise_difficulty_level.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class ExerciseDifficultyLevel(Document):
- pass
diff --git a/erpnext/healthcare/doctype/exercise_difficulty_level/test_exercise_difficulty_level.py b/erpnext/healthcare/doctype/exercise_difficulty_level/test_exercise_difficulty_level.py
deleted file mode 100644
index 80ef3a7..0000000
--- a/erpnext/healthcare/doctype/exercise_difficulty_level/test_exercise_difficulty_level.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestExerciseDifficultyLevel(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/exercise_type/__init__.py b/erpnext/healthcare/doctype/exercise_type/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/exercise_type/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/exercise_type/exercise_type.js b/erpnext/healthcare/doctype/exercise_type/exercise_type.js
deleted file mode 100644
index 0614604..0000000
--- a/erpnext/healthcare/doctype/exercise_type/exercise_type.js
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Exercise Type', {
- refresh: function(frm) {
- let wrapper = frm.fields_dict.steps_html.wrapper;
-
- frm.ExerciseEditor = new erpnext.ExerciseEditor(frm, wrapper);
- }
-});
-
-erpnext.ExerciseEditor = class ExerciseEditor {
- constructor(frm, wrapper) {
- this.wrapper = wrapper;
- this.frm = frm;
- this.make(frm, wrapper);
- }
-
- make(frm, wrapper) {
- $(this.wrapper).empty();
-
- this.exercise_toolbar = $('<p>\
- <button class="btn btn-default btn-add btn-xs" style="margin-left: 10px;"></button>').appendTo(this.wrapper);
-
- this.exercise_cards = $('<div class="exercise-cards"></div>').appendTo(this.wrapper);
-
- this.row = $('<div class="exercise-row"></div>').appendTo(this.wrapper);
-
- let me = this;
-
- this.exercise_toolbar.find(".btn-add")
- .html(__('Add'))
- .on("click", function() {
- me.show_add_card_dialog(frm);
- });
-
- if (frm.doc.steps_table && frm.doc.steps_table.length > 0) {
- this.make_cards(frm);
- this.make_buttons(frm);
- }
- }
-
- make_cards(frm) {
- var me = this;
- $(me.exercise_cards).empty();
-
- $.each(frm.doc.steps_table, function(i, step) {
- $(repl(`
- <div class="exercise-col col-sm-4" id="%(col_id)s">
- <div class="card h-100 exercise-card" id="%(card_id)s">
- <div class="card-body exercise-card-body">
- <img src=%(image_src)s class="card-img-top" alt="...">
- <h4 class="card-title">%(title)s</h4>
- <p class="card-text text-truncate">%(description)s</p>
- </div>
- <div class="card-footer">
- <button class="btn btn-default btn-xs btn-edit" data-id="%(id)s"><i class="fa fa-pencil" aria-hidden="true"></i></button>
- <button class="btn btn-default btn-xs btn-del" data-id="%(id)s"><i class="fa fa-trash" aria-hidden="true"></i></button>
- </div>
- </div>
- </div>`, {image_src: step.image, title: step.title, description: step.description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
- });
- }
-
- make_buttons(frm) {
- let me = this;
- $('.btn-edit').on('click', function() {
- let id = $(this).attr('data-id');
- me.show_edit_card_dialog(frm, id);
- });
-
- $('.btn-del').on('click', function() {
- let id = $(this).attr('data-id');
- $('#card-'+id).addClass("zoom-out");
-
- setTimeout(() => {
- // not using grid_rows[id].remove because
- // grid_rows is not defined when the table is hidden
- frm.doc.steps_table.pop(id);
- frm.refresh_field('steps_table');
- $('#col-'+id).remove();
- frm.dirty();
- }, 300);
- });
- }
-
- show_add_card_dialog(frm) {
- let me = this;
- let d = new frappe.ui.Dialog({
- title: __('Add Exercise Step'),
- fields: [
- {
- "label": "Title",
- "fieldname": "title",
- "fieldtype": "Data",
- "reqd": 1
- },
- {
- "label": "Attach Image",
- "fieldname": "image",
- "fieldtype": "Attach Image"
- },
- {
- "label": "Step Description",
- "fieldname": "step_description",
- "fieldtype": "Long Text"
- }
- ],
- primary_action: function() {
- let data = d.get_values();
- let i = 0;
- if (frm.doc.steps_table) {
- i = frm.doc.steps_table.length;
- }
- $(repl(`
- <div class="exercise-col col-sm-4" id="%(col_id)s">
- <div class="card h-100 exercise-card" id="%(card_id)s">
- <div class="card-body exercise-card-body">
- <img src=%(image_src)s class="card-img-top" alt="...">
- <h4 class="card-title">%(title)s</h4>
- <p class="card-text text-truncate">%(description)s</p>
- </div>
- <div class="card-footer">
- <button class="btn btn-default btn-xs btn-edit" data-id="%(id)s"><i class="fa fa-pencil" aria-hidden="true"></i></button>
- <button class="btn btn-default btn-xs btn-del" data-id="%(id)s"><i class="fa fa-trash" aria-hidden="true"></i></button>
- </div>
- </div>
- </div>`, {image_src: data.image, title: data.title, description: data.step_description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
- let step = frappe.model.add_child(frm.doc, 'Exercise Type Step', 'steps_table');
- step.title = data.title;
- step.image = data.image;
- step.description = data.step_description;
- me.make_buttons(frm);
- frm.refresh_field('steps_table');
- d.hide();
- },
- primary_action_label: __('Add')
- });
- d.show();
- }
-
- show_edit_card_dialog(frm, id) {
- let new_dialog = new frappe.ui.Dialog({
- title: __("Edit Exercise Step"),
- fields: [
- {
- "label": "Title",
- "fieldname": "title",
- "fieldtype": "Data",
- "reqd": 1
- },
- {
- "label": "Attach Image",
- "fieldname": "image",
- "fieldtype": "Attach Image"
- },
- {
- "label": "Step Description",
- "fieldname": "step_description",
- "fieldtype": "Long Text"
- }
- ],
- primary_action: () => {
- let data = new_dialog.get_values();
- $('#card-'+id).find('.card-title').html(data.title);
- $('#card-'+id).find('img').attr('src', data.image);
- $('#card-'+id).find('.card-text').html(data.step_description);
-
- frm.doc.steps_table[id].title = data.title;
- frm.doc.steps_table[id].image = data.image;
- frm.doc.steps_table[id].description = data.step_description;
- refresh_field('steps_table');
- frm.dirty();
- new_dialog.hide();
- },
- primary_action_label: __("Edit"),
- });
-
- new_dialog.set_values({
- title: frm.doc.steps_table[id].title,
- image: frm.doc.steps_table[id].image,
- step_description: frm.doc.steps_table[id].description
- });
- new_dialog.show();
- }
-};
diff --git a/erpnext/healthcare/doctype/exercise_type/exercise_type.json b/erpnext/healthcare/doctype/exercise_type/exercise_type.json
deleted file mode 100644
index 0db9c6e..0000000
--- a/erpnext/healthcare/doctype/exercise_type/exercise_type.json
+++ /dev/null
@@ -1,144 +0,0 @@
-{
- "actions": [],
- "creation": "2020-03-29 21:37:03.366344",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "exercise_name",
- "body_parts",
- "column_break_3",
- "difficulty_level",
- "section_break_5",
- "description",
- "section_break_7",
- "exercise_steps",
- "column_break_9",
- "exercise_video",
- "section_break_11",
- "steps_html",
- "section_break_13",
- "steps_table"
- ],
- "fields": [
- {
- "fieldname": "exercise_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Exercise Name",
- "reqd": 1
- },
- {
- "fieldname": "difficulty_level",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Difficulty Level",
- "options": "Exercise Difficulty Level"
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_5",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "description",
- "fieldtype": "Long Text",
- "label": "Description"
- },
- {
- "fieldname": "section_break_7",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "exercise_steps",
- "fieldtype": "Attach",
- "label": "Exercise Instructions"
- },
- {
- "fieldname": "exercise_video",
- "fieldtype": "Link",
- "label": "Exercise Video",
- "options": "Video"
- },
- {
- "fieldname": "column_break_9",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "steps_html",
- "fieldtype": "HTML",
- "label": "Steps"
- },
- {
- "fieldname": "steps_table",
- "fieldtype": "Table",
- "hidden": 1,
- "label": "Steps Table",
- "options": "Exercise Type Step"
- },
- {
- "fieldname": "section_break_11",
- "fieldtype": "Section Break",
- "label": "Exercise Steps"
- },
- {
- "fieldname": "section_break_13",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "body_parts",
- "fieldtype": "Table MultiSelect",
- "label": "Body Parts",
- "options": "Body Part Link"
- }
- ],
- "links": [],
- "modified": "2020-04-21 13:05:36.555060",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Exercise Type",
- "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": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "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/healthcare/doctype/exercise_type/exercise_type.py b/erpnext/healthcare/doctype/exercise_type/exercise_type.py
deleted file mode 100644
index fb635c8..0000000
--- a/erpnext/healthcare/doctype/exercise_type/exercise_type.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class ExerciseType(Document):
- def autoname(self):
- if self.difficulty_level:
- self.name = ' - '.join(filter(None, [self.exercise_name, self.difficulty_level]))
- else:
- self.name = self.exercise_name
-
diff --git a/erpnext/healthcare/doctype/exercise_type/test_exercise_type.py b/erpnext/healthcare/doctype/exercise_type/test_exercise_type.py
deleted file mode 100644
index bf217e8..0000000
--- a/erpnext/healthcare/doctype/exercise_type/test_exercise_type.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestExerciseType(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/exercise_type_step/__init__.py b/erpnext/healthcare/doctype/exercise_type_step/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/exercise_type_step/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/exercise_type_step/exercise_type_step.json b/erpnext/healthcare/doctype/exercise_type_step/exercise_type_step.json
deleted file mode 100644
index b37ff00..0000000
--- a/erpnext/healthcare/doctype/exercise_type_step/exercise_type_step.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "actions": [],
- "creation": "2020-03-31 23:01:18.761967",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "title",
- "image",
- "description"
- ],
- "fields": [
- {
- "fieldname": "title",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Title",
- "reqd": 1
- },
- {
- "fieldname": "image",
- "fieldtype": "Attach Image",
- "label": "Image"
- },
- {
- "fieldname": "description",
- "fieldtype": "Long Text",
- "in_list_view": 1,
- "label": "Description"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-04-02 20:39:34.258512",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Exercise Type Step",
- "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/healthcare/doctype/exercise_type_step/exercise_type_step.py b/erpnext/healthcare/doctype/exercise_type_step/exercise_type_step.py
deleted file mode 100644
index 13d7e57..0000000
--- a/erpnext/healthcare/doctype/exercise_type_step/exercise_type_step.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class ExerciseTypeStep(Document):
- pass
diff --git a/erpnext/healthcare/doctype/fee_validity/__init__.py b/erpnext/healthcare/doctype/fee_validity/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/fee_validity/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.js b/erpnext/healthcare/doctype/fee_validity/fee_validity.js
deleted file mode 100644
index 7ea2213..0000000
--- a/erpnext/healthcare/doctype/fee_validity/fee_validity.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Fee Validity', {
-});
diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.json b/erpnext/healthcare/doctype/fee_validity/fee_validity.json
deleted file mode 100644
index b001bf0..0000000
--- a/erpnext/healthcare/doctype/fee_validity/fee_validity.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "beta": 1,
- "creation": "2017-01-05 10:56:29.564806",
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "practitioner",
- "patient",
- "column_break_3",
- "status",
- "section_break_5",
- "section_break_3",
- "max_visits",
- "visited",
- "ref_appointments",
- "column_break_6",
- "start_date",
- "valid_till"
- ],
- "fields": [
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "read_only": 1,
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "read_only": 1,
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "visited",
- "fieldtype": "Int",
- "label": "Visited yet",
- "read_only": 1
- },
- {
- "fieldname": "valid_till",
- "fieldtype": "Date",
- "label": "Valid till",
- "read_only": 1
- },
- {
- "fieldname": "section_break_3",
- "fieldtype": "Section Break",
- "label": "Validity",
- "read_only": 1
- },
- {
- "fieldname": "column_break_6",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "max_visits",
- "fieldtype": "Int",
- "label": "Max number of visit",
- "read_only": 1
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Status",
- "options": "Completed\nPending",
- "read_only": 1
- },
- {
- "fetch_from": "ref_appointment.appointment_date",
- "fieldname": "start_date",
- "fieldtype": "Date",
- "label": "Start Date",
- "read_only": 1
- },
- {
- "fieldname": "ref_appointments",
- "fieldtype": "Table MultiSelect",
- "label": "Reference Appointments",
- "options": "Fee Validity Reference",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_5",
- "fieldtype": "Section Break"
- }
- ],
- "in_create": 1,
- "links": [],
- "modified": "2020-03-17 20:25:06.487418",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Fee Validity",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "search_fields": "practitioner, patient",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "practitioner"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py
deleted file mode 100644
index 058bc97..0000000
--- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-import frappe
-from frappe.utils import getdate
-import datetime
-
-class FeeValidity(Document):
- def validate(self):
- self.update_status()
- self.set_start_date()
-
- def update_status(self):
- if self.visited >= self.max_visits:
- self.status = 'Completed'
- else:
- self.status = 'Pending'
-
- def set_start_date(self):
- self.start_date = getdate()
- for appointment in self.ref_appointments:
- appointment_date = frappe.db.get_value('Patient Appointment', appointment.appointment, 'appointment_date')
- if getdate(appointment_date) < self.start_date:
- self.start_date = getdate(appointment_date)
-
-
-def create_fee_validity(appointment):
- if not check_is_new_patient(appointment):
- return
-
- fee_validity = frappe.new_doc('Fee Validity')
- fee_validity.practitioner = appointment.practitioner
- fee_validity.patient = appointment.patient
- fee_validity.max_visits = frappe.db.get_single_value('Healthcare Settings', 'max_visits') or 1
- valid_days = frappe.db.get_single_value('Healthcare Settings', 'valid_days') or 1
- fee_validity.visited = 1
- fee_validity.valid_till = getdate(appointment.appointment_date) + datetime.timedelta(days=int(valid_days))
- fee_validity.append('ref_appointments', {
- 'appointment': appointment.name
- })
- fee_validity.save(ignore_permissions=True)
- return fee_validity
-
-def check_is_new_patient(appointment):
- validity_exists = frappe.db.exists('Fee Validity', {
- 'practitioner': appointment.practitioner,
- 'patient': appointment.patient
- })
- if validity_exists:
- return False
-
- appointment_exists = frappe.db.get_all('Patient Appointment', {
- 'name': ('!=', appointment.name),
- 'status': ('!=', 'Cancelled'),
- 'patient': appointment.patient,
- 'practitioner': appointment.practitioner
- })
- if len(appointment_exists) and appointment_exists[0]:
- return False
- return True
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js
deleted file mode 100644
index 0ebb974..0000000
--- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Fee Validity", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Fee Validity
- () => frappe.tests.make('Fee Validity', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
deleted file mode 100644
index 7e7fd82..0000000
--- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-from frappe.utils import nowdate, add_days
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_appointment, create_healthcare_service_items
-from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
-
-test_dependencies = ["Company"]
-
-class TestFeeValidity(unittest.TestCase):
- def setUp(self):
- frappe.db.sql("""delete from `tabPatient Appointment`""")
- frappe.db.sql("""delete from `tabFee Validity`""")
- frappe.db.sql("""delete from `tabPatient`""")
- make_pos_profile()
-
- def test_fee_validity(self):
- item = create_healthcare_service_items()
- healthcare_settings = frappe.get_single("Healthcare Settings")
- healthcare_settings.enable_free_follow_ups = 1
- healthcare_settings.max_visits = 2
- healthcare_settings.valid_days = 7
- healthcare_settings.automate_appointment_invoicing = 1
- healthcare_settings.op_consulting_charge_item = item
- healthcare_settings.save(ignore_permissions=True)
- patient, medical_department, practitioner = create_healthcare_docs()
-
- # appointment should not be invoiced. Check Fee Validity created for new patient
- appointment = create_appointment(patient, practitioner, nowdate())
- invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
- self.assertEqual(invoiced, 0)
-
- # appointment should not be invoiced as it is within fee validity
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4))
- invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
- self.assertEqual(invoiced, 0)
-
- # appointment should be invoiced as it is within fee validity but the max_visits are exceeded
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 5), invoice=1)
- invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
- self.assertEqual(invoiced, 1)
-
- # appointment should be invoiced as it is not within fee validity and the max_visits are exceeded
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), invoice=1)
- invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced")
- self.assertEqual(invoiced, 1)
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/fee_validity_reference/__init__.py b/erpnext/healthcare/doctype/fee_validity_reference/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/fee_validity_reference/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.json b/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.json
deleted file mode 100644
index 40f128e..0000000
--- a/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "actions": [],
- "creation": "2020-03-13 16:08:42.859996",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "appointment"
- ],
- "fields": [
- {
- "fieldname": "appointment",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient Appointment",
- "options": "Patient Appointment",
- "reqd": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-03-15 00:27:02.076470",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Fee Validity Reference",
- "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/healthcare/doctype/fee_validity_reference/fee_validity_reference.py b/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.py
deleted file mode 100644
index c819280..0000000
--- a/erpnext/healthcare/doctype/fee_validity_reference/fee_validity_reference.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class FeeValidityReference(Document):
- pass
diff --git a/erpnext/healthcare/doctype/healthcare.py b/erpnext/healthcare/doctype/healthcare.py
deleted file mode 100644
index 6fd2015..0000000
--- a/erpnext/healthcare/doctype/healthcare.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import unicode_literals
-
-def get_data():
-
- return []
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/__init__.py b/erpnext/healthcare/doctype/healthcare_practitioner/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js
deleted file mode 100644
index fc0b241..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Healthcare Practitioner', {
- setup: function(frm) {
- frm.set_query('account', 'accounts', function(doc, cdt, cdn) {
- let d = locals[cdt][cdn];
- return {
- filters: {
- 'root_type': 'Income',
- 'company': d.company,
- 'is_group': 0
- }
- };
- });
- },
- refresh: function(frm) {
- frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Healthcare Practitioner'};
-
- if (!frm.is_new()) {
- frappe.contacts.render_address_and_contact(frm);
- } else {
- frappe.contacts.clear_address_and_contact(frm);
- }
-
- frm.set_query('service_unit', 'practitioner_schedules', function(){
- return {
- filters: {
- 'is_group': false,
- 'allow_appointments': true
- }
- };
- });
-
- set_query_service_item(frm, 'inpatient_visit_charge_item');
- set_query_service_item(frm, 'op_consulting_charge_item');
- }
-});
-
-let set_query_service_item = function(frm, service_item_field) {
- frm.set_query(service_item_field, function() {
- return {
- filters: {
- 'is_sales_item': 1,
- 'is_stock_item': 0
- }
- };
- });
-};
-
-frappe.ui.form.on('Healthcare Practitioner', 'user_id',function(frm) {
- if (frm.doc.user_id) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'User',
- name: frm.doc.user_id
- },
- callback: function (data) {
-
- frappe.model.get_value('Employee', {'user_id': frm.doc.user_id}, 'name',
- function(data) {
- if (data) {
- if (!frm.doc.employee || frm.doc.employee != data.name)
- frappe.model.set_value(frm.doctype, frm.docname, 'employee', data.name);
- } else {
- frappe.model.set_value(frm.doctype, frm.docname, 'employee', '');
- }
- }
- );
-
- if (!frm.doc.first_name || frm.doc.first_name != data.message.first_name)
- frappe.model.set_value(frm.doctype,frm.docname, 'first_name', data.message.first_name);
- if (!frm.doc.middle_name || frm.doc.middle_name != data.message.middle_name)
- frappe.model.set_value(frm.doctype,frm.docname, 'middle_name', data.message.middle_name);
- if (!frm.doc.last_name || frm.doc.last_name != data.message.last_name)
- frappe.model.set_value(frm.doctype,frm.docname, 'last_name', data.message.last_name);
- if (!frm.doc.mobile_phone || frm.doc.mobile_phone != data.message.mobile_no)
- frappe.model.set_value(frm.doctype,frm.docname, 'mobile_phone', data.message.mobile_no);
- }
- });
- }
-});
-
-frappe.ui.form.on('Healthcare Practitioner', 'employee', function(frm) {
- if (frm.doc.employee){
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Employee',
- name: frm.doc.employee
- },
- callback: function (data) {
- if (!frm.doc.user_id || frm.doc.user_id != data.message.user_id)
- frm.set_value('user_id', data.message.user_id);
- if (!frm.doc.designation || frm.doc.designation != data.message.designation)
- frappe.model.set_value(frm.doctype,frm.docname, 'designation', data.message.designation);
- if (!frm.doc.first_name || !frm.doc.user_id){
- frappe.model.set_value(frm.doctype,frm.docname, 'first_name', data.message.first_name);
- frappe.model.set_value(frm.doctype,frm.docname, 'middle_name', '');
- frappe.model.set_value(frm.doctype,frm.docname, 'last_name', data.message.last_name);
- }
- if (!frm.doc.mobile_phone || !frm.doc.user_id)
- frappe.model.set_value(frm.doctype,frm.docname, 'mobile_phone', data.message.cell_number);
- if (!frm.doc.address || frm.doc.address != data.message.current_address)
- frappe.model.set_value(frm.doctype,frm.docname, 'address', data.message.current_address);
- }
- });
- }
-});
-
-frappe.tour['Healthcare Practitioner'] = [
- {
- fieldname: 'employee',
- title: __('Employee'),
- description: __('If you want to track Payroll and other HRMS operations for a Practitoner, create an Employee and link it here.')
- },
- {
- fieldname: 'practitioner_schedules',
- title: __('Practitioner Schedules'),
- description: __('Set the Practitioner Schedule you just created. This will be used while booking appointments.')
- },
- {
- fieldname: 'op_consulting_charge_item',
- title: __('Out Patient Consulting Charge Item'),
- description: __('Create a service item for Out Patient Consulting.')
- },
- {
- fieldname: 'inpatient_visit_charge_item',
- title: __('Inpatient Visit Charge Item'),
- description: __('If this Healthcare Practitioner works for the In-Patient Department, create a service item for Inpatient Visits.')
- },
- {
- fieldname: 'op_consulting_charge',
- title: __('Out Patient Consulting Charge'),
- description: __('Set the Out Patient Consulting Charge for this Practitioner.')
-
- },
- {
- fieldname: 'inpatient_visit_charge',
- title: __('Inpatient Visit Charge'),
- description: __('If this Healthcare Practitioner also works for the In-Patient Department, set the inpatient visit charge for this Practitioner.')
- }
-];
-
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
deleted file mode 100644
index 8162f03..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
+++ /dev/null
@@ -1,333 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2016-02-23 11:20:53.565119",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
- "basic_details_section",
- "naming_series",
- "first_name",
- "middle_name",
- "last_name",
- "practitioner_name",
- "gender",
- "image",
- "column_break_7",
- "status",
- "mobile_phone",
- "residence_phone",
- "office_phone",
- "employee_and_user_details_section",
- "employee",
- "department",
- "designation",
- "column_break_17",
- "user_id",
- "hospital",
- "appointments",
- "practitioner_schedules",
- "charges",
- "op_consulting_charge_item",
- "op_consulting_charge",
- "column_break_18",
- "inpatient_visit_charge_item",
- "inpatient_visit_charge",
- "account_details",
- "default_currency",
- "accounts",
- "address_and_contacts_section",
- "address_html",
- "column_break_19",
- "contact_html"
- ],
- "fields": [
- {
- "fieldname": "first_name",
- "fieldtype": "Data",
- "label": "First Name",
- "no_copy": 1,
- "reqd": 1
- },
- {
- "fieldname": "middle_name",
- "fieldtype": "Data",
- "label": "Middle Name (Optional)",
- "no_copy": 1
- },
- {
- "fieldname": "last_name",
- "fieldtype": "Data",
- "label": "Last Name",
- "no_copy": 1
- },
- {
- "fieldname": "image",
- "fieldtype": "Attach Image",
- "hidden": 1,
- "label": "Image",
- "no_copy": 1,
- "print_hide": 1
- },
- {
- "fieldname": "employee",
- "fieldtype": "Link",
- "label": "Employee",
- "options": "Employee"
- },
- {
- "fieldname": "user_id",
- "fieldtype": "Link",
- "label": "User",
- "options": "User",
- "search_index": 1
- },
- {
- "fetch_from": "employee",
- "fieldname": "designation",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Designation",
- "options": "Designation",
- "read_only": 1
- },
- {
- "fieldname": "department",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Medical Department",
- "options": "Medical Department"
- },
- {
- "fieldname": "column_break_7",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "hospital",
- "fieldtype": "Data",
- "label": "Hospital"
- },
- {
- "fieldname": "mobile_phone",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Mobile"
- },
- {
- "fieldname": "residence_phone",
- "fieldtype": "Data",
- "label": "Phone (R)"
- },
- {
- "fieldname": "office_phone",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Phone (Office)"
- },
- {
- "collapsible": 1,
- "fieldname": "appointments",
- "fieldtype": "Section Break",
- "label": "Appointments"
- },
- {
- "fieldname": "practitioner_schedules",
- "fieldtype": "Table",
- "label": "Practitioner Schedules",
- "options": "Practitioner Service Unit Schedule"
- },
- {
- "collapsible": 1,
- "fieldname": "charges",
- "fieldtype": "Section Break",
- "label": "Charges"
- },
- {
- "fieldname": "op_consulting_charge_item",
- "fieldtype": "Link",
- "label": "Out Patient Consulting Charge Item",
- "options": "Item"
- },
- {
- "fieldname": "op_consulting_charge",
- "fieldtype": "Currency",
- "label": "Out Patient Consulting Charge",
- "mandatory_depends_on": "op_consulting_charge_item",
- "options": "Currency"
- },
- {
- "fieldname": "column_break_18",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "inpatient_visit_charge_item",
- "fieldtype": "Link",
- "label": "Inpatient Visit Charge Item",
- "options": "Item"
- },
- {
- "fieldname": "inpatient_visit_charge",
- "fieldtype": "Currency",
- "label": "Inpatient Visit Charge",
- "mandatory_depends_on": "inpatient_visit_charge_item"
- },
- {
- "depends_on": "eval: !doc.__islocal",
- "fieldname": "address_html",
- "fieldtype": "HTML",
- "label": "Address HTML",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "column_break_19",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval: !doc.__islocal",
- "fieldname": "contact_html",
- "fieldtype": "HTML",
- "label": "Contact HTML",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "account_details",
- "fieldtype": "Section Break",
- "label": "Account Details"
- },
- {
- "fieldname": "accounts",
- "fieldtype": "Table",
- "label": "Income Account",
- "options": "Party Account"
- },
- {
- "fieldname": "default_currency",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Default Currency",
- "no_copy": 1,
- "options": "Currency",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "bold": 1,
- "fieldname": "practitioner_name",
- "fieldtype": "Data",
- "in_global_search": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Full Name",
- "no_copy": 1,
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "no_copy": 1,
- "options": "HLC-PRAC-.YYYY.-",
- "report_hide": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "gender",
- "fieldtype": "Link",
- "label": "Gender",
- "options": "Gender"
- },
- {
- "fieldname": "employee_and_user_details_section",
- "fieldtype": "Section Break",
- "label": "Employee and User Details"
- },
- {
- "fieldname": "column_break_17",
- "fieldtype": "Column Break"
- },
- {
- "default": "Active",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Status",
- "options": "\nActive\nDisabled",
- "reqd": 1
- },
- {
- "fieldname": "basic_details_section",
- "fieldtype": "Section Break",
- "label": "Basic Details"
- },
- {
- "collapsible": 1,
- "depends_on": "eval: !doc.__islocal",
- "fieldname": "address_and_contacts_section",
- "fieldtype": "Section Break",
- "label": "Address and Contacts"
- }
- ],
- "image_field": "image",
- "links": [],
- "modified": "2021-01-22 10:14:43.187675",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare Practitioner",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "practitioner_name, mobile_phone, office_phone",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "practitioner_name",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py
deleted file mode 100644
index 5da5a06..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe import _
-from erpnext.accounts.party import validate_party_accounts
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
-from frappe.model.naming import append_number_if_name_exists
-from frappe.desk.reportview import build_match_conditions, get_filters_cond
-
-class HealthcarePractitioner(Document):
- def onload(self):
- load_address_and_contact(self)
-
- def autoname(self):
- # concat first and last name
- self.name = self.practitioner_name
-
- if frappe.db.exists('Healthcare Practitioner', self.name):
- self.name = append_number_if_name_exists('Contact', self.name)
-
- def validate(self):
- self.set_full_name()
- validate_party_accounts(self)
- if self.inpatient_visit_charge_item:
- validate_service_item(self.inpatient_visit_charge_item, 'Configure a service Item for Inpatient Consulting Charge Item')
- if self.op_consulting_charge_item:
- validate_service_item(self.op_consulting_charge_item, 'Configure a service Item for Out Patient Consulting Charge Item')
-
- if self.user_id:
- self.validate_user_id()
- else:
- existing_user_id = frappe.db.get_value('Healthcare Practitioner', self.name, 'user_id')
- if existing_user_id:
- frappe.permissions.remove_user_permission(
- 'Healthcare Practitioner', self.name, existing_user_id)
-
- def on_update(self):
- if self.user_id:
- frappe.permissions.add_user_permission('Healthcare Practitioner', self.name, self.user_id)
-
- def set_full_name(self):
- if self.last_name:
- self.practitioner_name = ' '.join(filter(None, [self.first_name, self.last_name]))
- else:
- self.practitioner_name = self.first_name
-
- def validate_user_id(self):
- if not frappe.db.exists('User', self.user_id):
- frappe.throw(_('User {0} does not exist').format(self.user_id))
- elif not frappe.db.exists('User', self.user_id, 'enabled'):
- frappe.throw(_('User {0} is disabled').format(self.user_id))
-
- # check duplicate
- practitioner = frappe.db.exists('Healthcare Practitioner', {
- 'user_id': self.user_id,
- 'name': ('!=', self.name)
- })
- if practitioner:
- frappe.throw(_('User {0} is already assigned to Healthcare Practitioner {1}').format(
- self.user_id, practitioner))
-
- def on_trash(self):
- delete_contact_and_address('Healthcare Practitioner', self.name)
-
-def validate_service_item(item, msg):
- if frappe.db.get_value('Item', item, 'is_stock_item'):
- frappe.throw(_(msg))
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None):
- fields = ['name', 'practitioner_name', 'mobile_phone']
-
- filters = {
- 'name': ('like', '%%%s%%' % txt)
- }
-
- return frappe.get_all('Healthcare Practitioner', fields = fields,
- filters = filters, start=start, page_length=page_len, order_by='name, practitioner_name', as_list=1)
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py
deleted file mode 100644
index bcee444..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner_dashboard.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'heatmap': True,
- 'heatmap_message': _('This is based on transactions against this Healthcare Practitioner.'),
- 'fieldname': 'practitioner',
- 'transactions': [
- {
- 'label': _('Appointments and Patient Encounters'),
- 'items': ['Patient Appointment', 'Patient Encounter', 'Fee Validity']
- },
- {
- 'label': _('Consultation'),
- 'items': ['Clinical Procedure', 'Lab Test']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.js
deleted file mode 100644
index 75aa208..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Practitioner", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Healthcare Practitioner
- () => frappe.tests.make('Healthcare Practitioner', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.py
deleted file mode 100644
index de8201b..0000000
--- a/erpnext/healthcare/doctype/healthcare_practitioner/test_healthcare_practitioner.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestHealthcarePractitioner(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/__init__.py b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json
deleted file mode 100644
index cf54e82..0000000
--- a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json
+++ /dev/null
@@ -1,136 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2017-05-03 17:27:07.466088",
- "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": "day",
- "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": "Day",
- "length": 0,
- "no_copy": 0,
- "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday",
- "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,
- "fieldname": "from_time",
- "fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "From 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,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "to_time",
- "fieldtype": "Time",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "To 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,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare Schedule Time Slot",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "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/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.py b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.py
deleted file mode 100644
index e58ea53..0000000
--- a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class HealthcareScheduleTimeSlot(Document):
- pass
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/__init__.py b/erpnext/healthcare/doctype/healthcare_service_unit/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js
deleted file mode 100644
index 2cdd550..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Healthcare Service Unit', {
- onload: function(frm) {
- frm.list_route = 'Tree/Healthcare Service Unit';
-
- // get query select healthcare service unit
- frm.fields_dict['parent_healthcare_service_unit'].get_query = function(doc) {
- return{
- filters:[
- ['Healthcare Service Unit', 'is_group', '=', 1],
- ['Healthcare Service Unit', 'name', '!=', doc.healthcare_service_unit_name]
- ]
- };
- };
- },
- refresh: function(frm) {
- frm.trigger('set_root_readonly');
- frm.set_df_property('service_unit_type', 'reqd', 1);
- frm.add_custom_button(__('Healthcare Service Unit Tree'), function() {
- frappe.set_route('Tree', 'Healthcare Service Unit');
- });
- },
- set_root_readonly: function(frm) {
- // read-only for root healthcare service unit
- frm.set_intro('');
- if (!frm.doc.parent_healthcare_service_unit) {
- frm.set_read_only();
- frm.set_intro(__('This is a root healthcare service unit and cannot be edited.'), true);
- }
- },
- allow_appointments: function(frm) {
- if (!frm.doc.allow_appointments) {
- frm.set_value('overlap_appointments', false);
- }
- },
- is_group: function(frm) {
- if (frm.doc.is_group == 1) {
- frm.set_value('allow_appointments', false);
- frm.set_df_property('service_unit_type', 'reqd', 0);
- }
- else {
- frm.set_df_property('service_unit_type', 'reqd', 1);
- }
- }
-});
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json
deleted file mode 100644
index 9ee865a..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json
+++ /dev/null
@@ -1,216 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:healthcare_service_unit_name",
- "beta": 1,
- "creation": "2016-09-21 13:48:14.731437",
- "description": "Healthcare Service Unit",
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "healthcare_service_unit_name",
- "is_group",
- "service_unit_type",
- "allow_appointments",
- "overlap_appointments",
- "inpatient_occupancy",
- "occupancy_status",
- "column_break_9",
- "company",
- "warehouse",
- "tree_details_section",
- "parent_healthcare_service_unit",
- "lft",
- "rgt",
- "old_parent"
- ],
- "fields": [
- {
- "fieldname": "healthcare_service_unit_name",
- "fieldtype": "Data",
- "in_global_search": 1,
- "in_list_view": 1,
- "label": "Service Unit",
- "reqd": 1,
- "unique": 1
- },
- {
- "bold": 1,
- "fieldname": "parent_healthcare_service_unit",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Parent Service Unit",
- "options": "Healthcare Service Unit"
- },
- {
- "bold": 1,
- "default": "0",
- "depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1",
- "fieldname": "is_group",
- "fieldtype": "Check",
- "label": "Is Group"
- },
- {
- "bold": 1,
- "depends_on": "eval:doc.is_group != 1",
- "fieldname": "service_unit_type",
- "fieldtype": "Link",
- "label": "Service Unit Type",
- "options": "Healthcare Service Unit Type"
- },
- {
- "default": "0",
- "depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1",
- "fetch_from": "service_unit_type.allow_appointments",
- "fieldname": "allow_appointments",
- "fieldtype": "Check",
- "in_list_view": 1,
- "label": "Allow Appointments",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "default": "0",
- "depends_on": "eval:doc.is_group != 1 && doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
- "fetch_from": "service_unit_type.overlap_appointments",
- "fieldname": "overlap_appointments",
- "fieldtype": "Check",
- "label": "Allow Overlap",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "bold": 1,
- "default": "0",
- "depends_on": "eval:doc.allow_appointments != 1 && doc.is_group != 1",
- "fetch_from": "service_unit_type.inpatient_occupancy",
- "fieldname": "inpatient_occupancy",
- "fieldtype": "Check",
- "in_list_view": 1,
- "label": "Inpatient Occupancy",
- "no_copy": 1,
- "read_only": 1,
- "search_index": 1
- },
- {
- "depends_on": "eval:doc.inpatient_occupancy == 1",
- "fieldname": "occupancy_status",
- "fieldtype": "Select",
- "label": "Occupancy Status",
- "no_copy": 1,
- "options": "Vacant\nOccupied",
- "read_only": 1
- },
- {
- "fieldname": "column_break_9",
- "fieldtype": "Column Break"
- },
- {
- "bold": 1,
- "depends_on": "eval:doc.is_group != 1",
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "label": "Warehouse",
- "no_copy": 1,
- "options": "Warehouse"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company",
- "remember_last_selected_value": 1,
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "lft",
- "fieldtype": "Int",
- "hidden": 1,
- "label": "lft",
- "no_copy": 1,
- "print_hide": 1,
- "search_index": 1
- },
- {
- "fieldname": "rgt",
- "fieldtype": "Int",
- "hidden": 1,
- "label": "rgt",
- "no_copy": 1,
- "print_hide": 1,
- "search_index": 1
- },
- {
- "fieldname": "old_parent",
- "fieldtype": "Link",
- "hidden": 1,
- "ignore_user_permissions": 1,
- "label": "Old Parent",
- "no_copy": 1,
- "options": "Healthcare Service Unit",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "tree_details_section",
- "fieldtype": "Section Break",
- "label": "Tree Details"
- }
- ],
- "links": [],
- "modified": "2020-05-20 18:26:56.065543",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare Service Unit",
- "owner": "Administrator",
- "permissions": [
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "search_fields": "healthcare_service_unit_name",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "healthcare_service_unit_name",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py
deleted file mode 100644
index 9e0417a..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-from frappe.utils.nestedset import NestedSet
-import frappe
-
-class HealthcareServiceUnit(NestedSet):
- nsm_parent_field = 'parent_healthcare_service_unit'
-
- def autoname(self):
- if self.company:
- suffix = " - " + frappe.get_cached_value('Company', self.company, "abbr")
- if not self.healthcare_service_unit_name.endswith(suffix):
- self.name = self.healthcare_service_unit_name + suffix
- else:
- self.name = self.healthcare_service_unit_name
-
- def on_update(self):
- super(HealthcareServiceUnit, self).on_update()
- self.validate_one_root()
-
- def after_insert(self):
- if self.is_group:
- self.allow_appointments = 0
- self.overlap_appointments = 0
- self.inpatient_occupancy = 0
- elif self.service_unit_type:
- service_unit_type = frappe.get_doc('Healthcare Service Unit Type', self.service_unit_type)
- self.allow_appointments = service_unit_type.allow_appointments
- self.overlap_appointments = service_unit_type.overlap_appointments
- self.inpatient_occupancy = service_unit_type.inpatient_occupancy
- if self.inpatient_occupancy:
- self.occupancy_status = 'Vacant'
- self.overlap_appointments = 0
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js
deleted file mode 100644
index b75f271..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit_tree.js
+++ /dev/null
@@ -1,35 +0,0 @@
-frappe.treeview_settings["Healthcare Service Unit"] = {
- breadcrumbs: "Healthcare Service Unit",
- title: __("Healthcare Service Unit"),
- get_tree_root: false,
- filters: [{
- fieldname: "company",
- fieldtype: "Select",
- options: erpnext.utils.get_tree_options("company"),
- label: __("Company"),
- default: erpnext.utils.get_tree_default("company")
- }],
- get_tree_nodes: 'erpnext.healthcare.utils.get_children',
- ignore_fields:["parent_healthcare_service_unit"],
- onrender: function(node) {
- if (node.data.occupied_out_of_vacant!==undefined) {
- $('<span class="balance-area pull-right">'
- + " " + node.data.occupied_out_of_vacant
- + '</span>').insertBefore(node.$ul);
- }
- if (node.data && node.data.inpatient_occupancy!==undefined) {
- if (node.data.inpatient_occupancy == 1) {
- if (node.data.occupancy_status == "Occupied") {
- $('<span class="balance-area pull-right">'
- + " " + node.data.occupancy_status
- + '</span>').insertBefore(node.$ul);
- }
- if (node.data.occupancy_status == "Vacant") {
- $('<span class="balance-area pull-right">'
- + " " + node.data.occupancy_status
- + '</span>').insertBefore(node.$ul);
- }
- }
- }
- },
-};
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.js b/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.js
deleted file mode 100644
index a67a411..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Service Unit", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Healthcare Service Unit
- () => frappe.tests.make('Healthcare Service Unit', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.py
deleted file mode 100644
index bced2fe..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit/test_healthcare_service_unit.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-class TestHealthcareServiceUnit(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/__init__.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js
deleted file mode 100644
index eb33ab6..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.js
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Healthcare Service Unit Type', {
- refresh: function(frm) {
- frm.set_df_property('item_code', 'read_only', frm.doc.__islocal ? 0 : 1);
- if (!frm.doc.__islocal && frm.doc.is_billable) {
- frm.add_custom_button(__('Change Item Code'), function() {
- change_item_code(cur_frm, frm.doc);
- });
- }
- },
-
- service_unit_type: function(frm) {
- set_item_details(frm);
-
- if (!frm.doc.__islocal) {
- frm.doc.change_in_item = 1;
- }
- },
-
- is_billable: function(frm) {
- set_item_details(frm);
- },
-
- rate: function(frm) {
- if (!frm.doc.__islocal) {
- frm.doc.change_in_item = 1;
- }
- },
- item_group: function(frm) {
- if (!frm.doc.__islocal) {
- frm.doc.change_in_item = 1;
- }
- },
- description: function(frm) {
- if (!frm.doc.__islocal) {
- frm.doc.change_in_item = 1;
- }
- }
-});
-
-let set_item_details = function(frm) {
- if (frm.doc.service_unit_type && frm.doc.is_billable) {
- if (!frm.doc.item_code)
- frm.set_value('item_code', frm.doc.service_unit_type);
- if (!frm.doc.description)
- frm.set_value('description', frm.doc.service_unit_type);
- if (!frm.doc.item_group)
- frm.set_value('item_group', 'Services');
- }
-};
-
-let change_item_code = function(frm, doc) {
- let d = new frappe.ui.Dialog({
- title: __('Change Item Code'),
- fields: [
- {
- 'fieldtype': 'Data',
- 'label': 'Item Code',
- 'fieldname': 'item_code',
- 'default': doc.item_code,
- reqd: 1,
- }
- ],
- primary_action: function() {
- let values = d.get_values();
- if (values) {
- frappe.call({
- "method": "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.change_item_code",
- "args": {item: doc.item, item_code: values['item_code'], doc_name: doc.name},
- callback: function () {
- frm.reload_doc();
- }
- });
- }
- d.hide();
- },
- primary_action_label: __("Change Template Code")
- });
-
- d.show();
- d.set_values({
- 'Item Code': frm.doc.item_code
- });
-};
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json
deleted file mode 100644
index 4b8503d..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json
+++ /dev/null
@@ -1,164 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:service_unit_type",
- "creation": "2018-07-11 16:47:51.414675",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "disabled",
- "service_unit_type",
- "allow_appointments",
- "overlap_appointments",
- "inpatient_occupancy",
- "is_billable",
- "item_details",
- "item",
- "item_code",
- "item_group",
- "uom",
- "no_of_hours",
- "column_break_11",
- "rate",
- "description",
- "change_in_item"
- ],
- "fields": [
- {
- "fieldname": "service_unit_type",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Service Unit Type",
- "no_copy": 1,
- "reqd": 1,
- "unique": 1
- },
- {
- "bold": 1,
- "default": "0",
- "depends_on": "eval:doc.inpatient_occupancy != 1",
- "fieldname": "allow_appointments",
- "fieldtype": "Check",
- "label": "Allow Appointments"
- },
- {
- "bold": 1,
- "default": "0",
- "depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1",
- "fieldname": "overlap_appointments",
- "fieldtype": "Check",
- "label": "Allow Overlap"
- },
- {
- "bold": 1,
- "default": "0",
- "depends_on": "eval:doc.allow_appointments != 1",
- "fieldname": "inpatient_occupancy",
- "fieldtype": "Check",
- "label": "Inpatient Occupancy"
- },
- {
- "bold": 1,
- "default": "0",
- "depends_on": "eval:doc.inpatient_occupancy == 1 && doc.allow_appointments != 1",
- "fieldname": "is_billable",
- "fieldtype": "Check",
- "label": "Is Billable"
- },
- {
- "depends_on": "is_billable",
- "fieldname": "item_details",
- "fieldtype": "Section Break",
- "label": "Item Details"
- },
- {
- "fieldname": "item",
- "fieldtype": "Link",
- "label": "Item",
- "no_copy": 1,
- "options": "Item",
- "read_only": 1
- },
- {
- "fieldname": "item_code",
- "fieldtype": "Data",
- "label": "Item Code",
- "mandatory_depends_on": "eval: doc.is_billable == 1",
- "no_copy": 1
- },
- {
- "fieldname": "item_group",
- "fieldtype": "Link",
- "label": "Item Group",
- "mandatory_depends_on": "eval: doc.is_billable == 1",
- "options": "Item Group"
- },
- {
- "fieldname": "uom",
- "fieldtype": "Link",
- "label": "UOM",
- "mandatory_depends_on": "eval: doc.is_billable == 1",
- "options": "UOM"
- },
- {
- "fieldname": "no_of_hours",
- "fieldtype": "Int",
- "label": "UOM Conversion in Hours",
- "mandatory_depends_on": "eval: doc.is_billable == 1"
- },
- {
- "fieldname": "column_break_11",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "rate",
- "fieldtype": "Currency",
- "label": "Rate / UOM"
- },
- {
- "default": "0",
- "fieldname": "disabled",
- "fieldtype": "Check",
- "label": "Disabled",
- "no_copy": 1
- },
- {
- "fieldname": "description",
- "fieldtype": "Small Text",
- "label": "Description"
- },
- {
- "default": "0",
- "fieldname": "change_in_item",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Change in Item"
- }
- ],
- "links": [],
- "modified": "2020-05-20 15:31:09.627516",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare Service Unit Type",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "service_unit_type"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py
deleted file mode 100644
index a318e50..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.model.rename_doc import rename_doc
-
-class HealthcareServiceUnitType(Document):
- def validate(self):
- if self.allow_appointments and self.inpatient_occupancy:
- frappe.msgprint(
- _('Healthcare Service Unit Type cannot have both {0} and {1}').format(
- frappe.bold('Allow Appointments'), frappe.bold('Inpatient Occupancy')),
- raise_exception=1, title=_('Validation Error'), indicator='red'
- )
- elif not self.allow_appointments and not self.inpatient_occupancy:
- frappe.msgprint(
- _('Healthcare Service Unit Type must allow atleast one among {0} and {1}').format(
- frappe.bold('Allow Appointments'), frappe.bold('Inpatient Occupancy')),
- raise_exception=1, title=_('Validation Error'), indicator='red'
- )
-
- if not self.allow_appointments:
- self.overlap_appointments = 0
-
- if self.is_billable:
- if self.disabled:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
- else:
- frappe.db.set_value('Item', self.item, 'disabled', 0)
-
- def after_insert(self):
- if self.inpatient_occupancy and self.is_billable:
- create_item(self)
-
- def on_trash(self):
- if self.item:
- try:
- item = self.item
- self.db_set('item', '')
- frappe.delete_doc('Item', item)
- except Exception:
- frappe.throw(_('Not permitted. Please disable the Service Unit Type'))
-
- def on_update(self):
- if self.change_in_item and self.is_billable and self.item:
- update_item(self)
-
- item_price = item_price_exists(self)
-
- if not item_price:
- price_list_name = frappe.db.get_value('Price List', {'selling': 1})
- if self.rate:
- make_item_price(self.item_code, price_list_name, self.rate)
- else:
- make_item_price(self.item_code, price_list_name, 0.0)
- else:
- frappe.db.set_value('Item Price', item_price, 'price_list_rate', self.rate)
-
- frappe.db.set_value(self.doctype, self.name, 'change_in_item',0)
- elif not self.is_billable and self.item:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
- self.reload()
-
-
-def item_price_exists(doc):
- item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': doc.item_code})
- if len(item_price):
- return item_price[0][0]
- return False
-
-def create_item(doc):
- # insert item
- item = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': doc.item_code,
- 'item_name': doc.service_unit_type,
- 'item_group': doc.item_group,
- 'description': doc.description or doc.item_code,
- 'is_sales_item': 1,
- 'is_service_item': 1,
- 'is_purchase_item': 0,
- 'is_stock_item': 0,
- 'show_in_website': 0,
- 'is_pro_applicable': 0,
- 'disabled': 0,
- 'stock_uom': doc.uom
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- # insert item price
- # get item price list to insert item price
- price_list_name = frappe.db.get_value('Price List', {'selling': 1})
- if doc.rate:
- make_item_price(item.name, price_list_name, doc.rate)
- item.standard_rate = doc.rate
- else:
- make_item_price(item.name, price_list_name, 0.0)
- item.standard_rate = 0.0
-
- item.save(ignore_permissions=True)
-
- # Set item in the doc
- doc.db_set('item', item.name)
-
-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)
-
-def update_item(doc):
- item = frappe.get_doc("Item", doc.item)
- if item:
- item.update({
- "item_name": doc.service_unit_type,
- "item_group": doc.item_group,
- "disabled": 0,
- "standard_rate": doc.rate,
- "description": doc.description
- })
- item.db_update()
-
-@frappe.whitelist()
-def change_item_code(item, item_code, doc_name):
- if frappe.db.exists({'doctype': 'Item', 'item_code': item_code}):
- frappe.throw(_('Item with Item Code {0} already exists').format(item_code))
- else:
- rename_doc('Item', item, item_code, ignore_permissions=True)
- frappe.db.set_value('Healthcare Service Unit Type', doc_name, 'item_code', item_code)
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type_dashboard.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type_dashboard.py
deleted file mode 100644
index 0ac548b..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'service_unit_type',
- 'transactions': [
- {
- 'label': _('Healthcare Service Units'),
- 'items': ['Healthcare Service Unit']
- },
- ]
- }
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.js b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.js
deleted file mode 100644
index 6db8f9e..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Service Unit Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Healthcare Service Unit Type
- () => frappe.tests.make('Healthcare Service Unit Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py
deleted file mode 100644
index 01cf4b0..0000000
--- a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-import frappe
-
-class TestHealthcareServiceUnitType(unittest.TestCase):
- def test_item_creation(self):
- unit_type = get_unit_type()
- self.assertTrue(frappe.db.exists('Item', unit_type.item))
-
- # check item disabled
- unit_type.disabled = 1
- unit_type.save()
- self.assertEqual(frappe.db.get_value('Item', unit_type.item, 'disabled'), 1)
-
-
-def get_unit_type():
- if frappe.db.exists('Healthcare Service Unit Type', 'Inpatient Rooms'):
- return frappe.get_doc('Healthcare Service Unit Type', 'Inpatient Rooms')
-
- unit_type = frappe.new_doc('Healthcare Service Unit Type')
- unit_type.service_unit_type = 'Inpatient Rooms'
- unit_type.inpatient_occupancy = 1
- unit_type.is_billable = 1
- unit_type.item_code = 'Inpatient Rooms'
- unit_type.item_group = 'Services'
- unit_type.uom = 'Hour'
- unit_type.no_of_hours = 1
- unit_type.rate = 4000
- unit_type.save()
- return unit_type
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js
deleted file mode 100644
index cf2276f..0000000
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Healthcare Settings', {
- setup: function(frm) {
- frm.set_query('account', 'receivable_account', function(doc, cdt, cdn) {
- var d = locals[cdt][cdn];
- return {
- filters: {
- 'account_type': 'Receivable',
- 'company': d.company,
- 'is_group': 0
- }
- };
- });
- frm.set_query('account', 'income_account', function(doc, cdt, cdn) {
- var d = locals[cdt][cdn];
- return {
- filters: {
- 'root_type': 'Income',
- 'company': d.company,
- 'is_group': 0
- }
- };
- });
- set_query_service_item(frm, 'inpatient_visit_charge_item');
- set_query_service_item(frm, 'op_consulting_charge_item');
- set_query_service_item(frm, 'clinical_procedure_consumable_item');
- }
-});
-
-var set_query_service_item = function(frm, service_item_field) {
- frm.set_query(service_item_field, function() {
- return {
- filters: {
- 'is_sales_item': 1,
- 'is_stock_item': 0
- }
- };
- });
-};
-
-frappe.tour['Healthcare Settings'] = [
- {
- fieldname: 'link_customer_to_patient',
- title: __('Link Customer to Patient'),
- description: __('If checked, a customer will be created for every Patient. Patient Invoices will be created against this Customer. You can also select existing Customer while creating a Patient. This field is checked by default.')
- },
- {
- fieldname: 'collect_registration_fee',
- title: __('Collect Registration Fee'),
- description: __('If your Healthcare facility bills registrations of Patients, you can check this and set the Registration Fee in the field below. Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.')
- },
- {
- fieldname: 'automate_appointment_invoicing',
- title: __('Automate Appointment Invoicing'),
- description: __('Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.')
- },
- {
- fieldname: 'inpatient_visit_charge_item',
- title: __('Healthcare Service Items'),
- description: __('You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ') + "<a href='https://docs.erpnext.com/docs/user/manual/en/healthcare/healthcare_settings#2-default-healthcare-service-items' target='_blank'>here</a>" + __(' to know more')
- },
- {
- fieldname: 'income_account',
- title: __('Set up default Accounts for the Healthcare Facility'),
- description: __('If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.')
-
- },
- {
- fieldname: 'send_registration_msg',
- title: __('Out Patient SMS alerts'),
- description: __('If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ') + "<a href='https://docs.erpnext.com/docs/user/manual/en/healthcare/healthcare_settings#4-out-patient-sms-alerts' target='_blank'>here</a>" + __(' to know more')
- }
-];
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
deleted file mode 100644
index ddf1bce..0000000
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
+++ /dev/null
@@ -1,351 +0,0 @@
-{
- "actions": [],
- "beta": 1,
- "creation": "2017-05-09 11:26:22.337760",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "sb_op_settings",
- "patient_name_by",
- "link_customer_to_patient",
- "default_medical_code_standard",
- "column_break_9",
- "collect_registration_fee",
- "registration_fee",
- "automate_appointment_invoicing",
- "enable_free_follow_ups",
- "max_visits",
- "valid_days",
- "inpatient_settings_section",
- "allow_discharge_despite_unbilled_services",
- "do_not_bill_inpatient_encounters",
- "healthcare_service_items",
- "inpatient_visit_charge_item",
- "op_consulting_charge_item",
- "column_break_13",
- "clinical_procedure_consumable_item",
- "sb_in_ac",
- "income_account",
- "receivable_account",
- "out_patient_sms_alerts",
- "send_registration_msg",
- "registration_msg",
- "send_appointment_confirmation",
- "appointment_confirmation_msg",
- "avoid_confirmation",
- "column_break_16",
- "send_appointment_reminder",
- "appointment_reminder_msg",
- "remind_before",
- "sb_lab_settings",
- "create_lab_test_on_si_submit",
- "create_sample_collection_for_lab_test",
- "column_break_34",
- "lab_test_approval_required",
- "employee_name_and_designation_in_print",
- "custom_signature_in_print",
- "laboratory_sms_alerts",
- "sms_printed",
- "column_break_28",
- "sms_emailed"
- ],
- "fields": [
- {
- "fieldname": "sb_op_settings",
- "fieldtype": "Section Break",
- "label": "Out Patient Settings"
- },
- {
- "fieldname": "default_medical_code_standard",
- "fieldtype": "Link",
- "label": "Default Medical Code Standard",
- "options": "Medical Code Standard"
- },
- {
- "fieldname": "column_break_9",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "description": "Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.",
- "fieldname": "collect_registration_fee",
- "fieldtype": "Check",
- "label": "Collect Fee for Patient Registration"
- },
- {
- "depends_on": "collect_registration_fee",
- "fieldname": "registration_fee",
- "fieldtype": "Currency",
- "label": "Registration Fee",
- "mandatory_depends_on": "eval:doc.collect_registration_fee == 1",
- "options": "Currency"
- },
- {
- "depends_on": "eval:doc.enable_free_follow_ups == 1",
- "description": "Time period (Valid number of days) for free consultations",
- "fieldname": "valid_days",
- "fieldtype": "Int",
- "label": "Valid Number of Days",
- "mandatory_depends_on": "eval:doc.enable_free_follow_ups == 1"
- },
- {
- "collapsible": 1,
- "description": "You can configure default Items for billing consultation charges, procedure consumption items and inpatient visits",
- "fieldname": "healthcare_service_items",
- "fieldtype": "Section Break",
- "label": "Default Healthcare Service Items"
- },
- {
- "fieldname": "inpatient_visit_charge_item",
- "fieldtype": "Link",
- "label": "Inpatient Visit Charge Item",
- "options": "Item"
- },
- {
- "fieldname": "op_consulting_charge_item",
- "fieldtype": "Link",
- "label": "Out Patient Consulting Charge Item",
- "options": "Item"
- },
- {
- "fieldname": "column_break_13",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "clinical_procedure_consumable_item",
- "fieldtype": "Link",
- "label": "Clinical Procedure Consumable Item",
- "options": "Item"
- },
- {
- "collapsible": 1,
- "fieldname": "out_patient_sms_alerts",
- "fieldtype": "Section Break",
- "label": "Out Patient SMS Alerts"
- },
- {
- "fieldname": "column_break_16",
- "fieldtype": "Column Break"
- },
- {
- "collapsible": 1,
- "fieldname": "sb_in_ac",
- "fieldtype": "Section Break",
- "label": "Default Accounts"
- },
- {
- "description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.",
- "fieldname": "income_account",
- "fieldtype": "Table",
- "label": "Income Account",
- "options": "Party Account"
- },
- {
- "description": "Default receivable accounts to be used to book Appointment charges.",
- "fieldname": "receivable_account",
- "fieldtype": "Table",
- "label": "Receivable Account",
- "options": "Party Account"
- },
- {
- "collapsible": 1,
- "fieldname": "sb_lab_settings",
- "fieldtype": "Section Break",
- "label": "Laboratory Settings"
- },
- {
- "fieldname": "column_break_34",
- "fieldtype": "Column Break"
- },
- {
- "default": "1",
- "description": "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.",
- "fieldname": "employee_name_and_designation_in_print",
- "fieldtype": "Check",
- "label": "Employee name and designation in print"
- },
- {
- "depends_on": "eval:doc.employee_name_and_designation_in_print == '0'\n",
- "fieldname": "custom_signature_in_print",
- "fieldtype": "Small Text",
- "label": "Custom Signature in Print"
- },
- {
- "collapsible": 1,
- "fieldname": "laboratory_sms_alerts",
- "fieldtype": "Section Break",
- "label": "Laboratory SMS Alerts"
- },
- {
- "default": "Hello {{doc.patient}}, Your {{doc.lab_test_name}} result is ready with {{doc.company }}. \nThank You, Good day!",
- "fieldname": "sms_printed",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Result Printed Message"
- },
- {
- "fieldname": "column_break_28",
- "fieldtype": "Column Break"
- },
- {
- "default": "Hello {{doc.patient}}, Your {{doc.lab_test_name}} result has been emailed to {{doc.email}}. \n{{doc.company }}. \nThank You, Good day!",
- "fieldname": "sms_emailed",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Result Emailed Message"
- },
- {
- "default": "0",
- "description": "Checking this will restrict printing and emailing of Lab Test documents unless they have the status as Approved.",
- "fieldname": "lab_test_approval_required",
- "fieldtype": "Check",
- "label": "Do not print or email Lab Tests without Approval"
- },
- {
- "default": "1",
- "description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.",
- "fieldname": "link_customer_to_patient",
- "fieldtype": "Check",
- "label": "Link Customer to Patient"
- },
- {
- "default": "0",
- "description": "Checking this will create Lab Test(s) specified in the Sales Invoice on submission.",
- "fieldname": "create_lab_test_on_si_submit",
- "fieldtype": "Check",
- "label": "Create Lab Test(s) on Sales Invoice Submission"
- },
- {
- "default": "0",
- "description": "Checking this will create a Sample Collection document every time you create a Lab Test",
- "fieldname": "create_sample_collection_for_lab_test",
- "fieldtype": "Check",
- "label": "Create Sample Collection document for Lab Test"
- },
- {
- "fieldname": "patient_name_by",
- "fieldtype": "Select",
- "label": "Patient Name By",
- "options": "Patient Name\nNaming Series"
- },
- {
- "default": "0",
- "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter",
- "fieldname": "automate_appointment_invoicing",
- "fieldtype": "Check",
- "label": "Automate Appointment Invoicing"
- },
- {
- "default": "0",
- "fieldname": "send_registration_msg",
- "fieldtype": "Check",
- "label": "Patient Registration"
- },
- {
- "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.name}} . Please note this ID for future reference. \nThank You!",
- "depends_on": "send_registration_msg",
- "fieldname": "registration_msg",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Registration Message"
- },
- {
- "default": "0",
- "fieldname": "send_appointment_confirmation",
- "fieldtype": "Check",
- "label": "Appointment Confirmation"
- },
- {
- "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} on {{doc.appointment_datetime}} at {{doc.company}}.\nThank you, Good day!",
- "depends_on": "send_appointment_confirmation",
- "fieldname": "appointment_confirmation_msg",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Confirmation Message"
- },
- {
- "default": "0",
- "depends_on": "send_appointment_confirmation",
- "description": "Do not confirm if appointment is created for the same day",
- "fieldname": "avoid_confirmation",
- "fieldtype": "Check",
- "label": "Avoid Confirmation"
- },
- {
- "default": "0",
- "fieldname": "send_appointment_reminder",
- "fieldtype": "Check",
- "label": "Appointment Reminder"
- },
- {
- "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_datetime}} at {{doc.company}}.\nThank you, Good day!\n",
- "depends_on": "send_appointment_reminder",
- "fieldname": "appointment_reminder_msg",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Reminder Message"
- },
- {
- "depends_on": "send_appointment_reminder",
- "fieldname": "remind_before",
- "fieldtype": "Time",
- "label": "Remind Before"
- },
- {
- "depends_on": "eval:doc.enable_free_follow_ups == 1",
- "description": "The number of free follow ups (Patient Encounters in valid days) allowed",
- "fieldname": "max_visits",
- "fieldtype": "Int",
- "label": "Number of Patient Encounters in Valid Days",
- "mandatory_depends_on": "eval:doc.enable_free_follow_ups == 1"
- },
- {
- "default": "0",
- "fieldname": "enable_free_follow_ups",
- "fieldtype": "Check",
- "label": "Enable Free Follow-ups"
- },
- {
- "fieldname": "inpatient_settings_section",
- "fieldtype": "Section Break",
- "label": "Inpatient Settings"
- },
- {
- "default": "0",
- "fieldname": "allow_discharge_despite_unbilled_services",
- "fieldtype": "Check",
- "label": "Allow Discharge Despite Unbilled Healthcare Services"
- },
- {
- "default": "0",
- "fieldname": "do_not_bill_inpatient_encounters",
- "fieldtype": "Check",
- "label": "Do Not Bill Patient Encounters for Inpatients"
- }
- ],
- "issingle": 1,
- "links": [],
- "modified": "2021-01-13 09:04:35.877700",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare Settings",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py
deleted file mode 100644
index a16fceb..0000000
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.core.doctype.sms_settings.sms_settings import send_sms
-import json
-
-class HealthcareSettings(Document):
- def validate(self):
- for key in ['collect_registration_fee', 'link_customer_to_patient', 'patient_name_by',
- 'lab_test_approval_required', 'create_sample_collection_for_lab_test', 'default_medical_code_standard']:
- frappe.db.set_default(key, self.get(key, ""))
-
- if self.collect_registration_fee:
- if self.registration_fee <= 0:
- frappe.throw(_('Registration Fee cannot be negative or zero'))
-
- if self.inpatient_visit_charge_item:
- validate_service_item(self.inpatient_visit_charge_item)
- if self.op_consulting_charge_item:
- validate_service_item(self.op_consulting_charge_item)
- if self.clinical_procedure_consumable_item:
- validate_service_item(self.clinical_procedure_consumable_item)
-
-
-def validate_service_item(item):
- if frappe.db.get_value('Item', item, 'is_stock_item'):
- frappe.throw(_('Configure a service Item for {0}').format(item))
-
-@frappe.whitelist()
-def get_sms_text(doc):
- sms_text = {}
- doc = frappe.get_doc('Lab Test', doc)
- context = {'doc': doc, 'alert': doc, 'comments': None}
-
- emailed = frappe.db.get_value('Healthcare Settings', None, 'sms_emailed')
- sms_text['emailed'] = frappe.render_template(emailed, context)
-
- printed = frappe.db.get_value('Healthcare Settings', None, 'sms_printed')
- sms_text['printed'] = frappe.render_template(printed, context)
-
- return sms_text
-
-def send_registration_sms(doc):
- if frappe.db.get_single_value('Healthcare Settings', 'send_registration_msg'):
- if doc.mobile:
- context = {'doc': doc, 'alert': doc, 'comments': None}
- if doc.get('_comments'):
- context['comments'] = json.loads(doc.get('_comments'))
- messages = frappe.db.get_single_value('Healthcare Settings', 'registration_msg')
- messages = frappe.render_template(messages, context)
- number = [doc.mobile]
- send_sms(number,messages)
- else:
- frappe.msgprint(doc.name + ' has no mobile number to send registration SMS', alert=True)
-
-def get_receivable_account(company):
- receivable_account = get_account(None, 'receivable_account', 'Healthcare Settings', company)
- if receivable_account:
- return receivable_account
-
- return frappe.get_cached_value('Company', company, 'default_receivable_account')
-
-def get_income_account(practitioner, company):
- # check income account in Healthcare Practitioner
- if practitioner:
- income_account = get_account('Healthcare Practitioner', None, practitioner, company)
- if income_account:
- return income_account
-
- # else check income account in Healthcare Settings
- income_account = get_account(None, 'income_account', 'Healthcare Settings', company)
- if income_account:
- return income_account
-
- # else return default income account of company
- return frappe.get_cached_value('Company', company, 'default_income_account')
-
-def get_account(parent_type, parent_field, parent, company):
- if parent_type:
- return frappe.db.get_value('Party Account',
- {'parenttype': parent_type, 'parent': parent, 'company': company}, 'account')
-
- if parent_field:
- return frappe.db.get_value('Party Account',
- {'parentfield': parent_field, 'parent': parent, 'company': company}, 'account')
diff --git a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js
deleted file mode 100644
index ca10925..0000000
--- a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Healthcare Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Healthcare Settings
- () => frappe.tests.make('Healthcare Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.py b/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.py
deleted file mode 100644
index 1b620d5..0000000
--- a/erpnext/healthcare/doctype/healthcare_settings/test_healthcare_settings.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestHealthcareSettings(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/__init__.py b/erpnext/healthcare/doctype/inpatient_medication_entry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
deleted file mode 100644
index a7b06b1..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Inpatient Medication Entry', {
- refresh: function(frm) {
- // Ignore cancellation of doctype on cancel all
- frm.ignore_doctypes_on_cancel_all = ['Stock Entry'];
- frm.fields_dict['medication_orders'].grid.wrapper.find('.grid-add-row').hide();
-
- frm.set_query('item_code', () => {
- return {
- filters: {
- is_stock_item: 1
- }
- };
- });
-
- frm.set_query('drug_code', 'medication_orders', () => {
- return {
- filters: {
- is_stock_item: 1
- }
- };
- });
-
- frm.set_query('warehouse', () => {
- return {
- filters: {
- company: frm.doc.company
- }
- };
- });
-
- if (frm.doc.__islocal || frm.doc.docstatus !== 0 || !frm.doc.update_stock)
- return;
-
- frm.add_custom_button(__('Make Stock Entry'), function() {
- frappe.call({
- method: 'erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry.make_difference_stock_entry',
- args: { docname: frm.doc.name },
- freeze: true,
- callback: function(r) {
- if (r.message) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
- } else {
- frappe.msgprint({
- title: __('No Drug Shortage'),
- message: __('All the drugs are available with sufficient qty to process this Inpatient Medication Entry.'),
- indicator: 'green'
- });
- }
- }
- });
- });
- },
-
- patient: function(frm) {
- if (frm.doc.patient)
- frm.set_value('service_unit', '');
- },
-
- get_medication_orders: function(frm) {
- frappe.call({
- method: 'get_medication_orders',
- doc: frm.doc,
- freeze: true,
- freeze_message: __('Fetching Pending Medication Orders'),
- callback: function() {
- refresh_field('medication_orders');
- }
- });
- }
-});
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
deleted file mode 100644
index b1a6ee4..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
+++ /dev/null
@@ -1,204 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2020-09-25 14:13:20.111906",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "company",
- "column_break_3",
- "posting_date",
- "status",
- "filters_section",
- "item_code",
- "assigned_to_practitioner",
- "patient",
- "practitioner",
- "service_unit",
- "column_break_11",
- "from_date",
- "to_date",
- "from_time",
- "to_time",
- "select_medication_orders_section",
- "get_medication_orders",
- "medication_orders",
- "section_break_18",
- "update_stock",
- "warehouse",
- "amended_from"
- ],
- "fields": [
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Naming Series",
- "options": "HLC-IME-.YYYY.-"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company",
- "reqd": 1
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "posting_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Posting Date",
- "reqd": 1
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "label": "Status",
- "options": "\nDraft\nSubmitted\nPending\nIn Process\nCompleted\nCancelled",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "eval: doc.__islocal",
- "fieldname": "filters_section",
- "fieldtype": "Section Break",
- "label": "Filters"
- },
- {
- "fieldname": "item_code",
- "fieldtype": "Link",
- "label": "Item Code (Drug)",
- "options": "Item"
- },
- {
- "depends_on": "update_stock",
- "description": "Warehouse from where medication stock should be consumed",
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "label": "Medication Warehouse",
- "mandatory_depends_on": "update_stock",
- "options": "Warehouse"
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "label": "Patient",
- "options": "Patient"
- },
- {
- "depends_on": "eval:!doc.patient",
- "fieldname": "service_unit",
- "fieldtype": "Link",
- "label": "Healthcare Service Unit",
- "options": "Healthcare Service Unit"
- },
- {
- "fieldname": "column_break_11",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "from_date",
- "fieldtype": "Date",
- "label": "From Date"
- },
- {
- "fieldname": "to_date",
- "fieldtype": "Date",
- "label": "To Date"
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Inpatient Medication Entry",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "select_medication_orders_section",
- "fieldtype": "Section Break",
- "label": "Medication Orders"
- },
- {
- "fieldname": "medication_orders",
- "fieldtype": "Table",
- "label": "Inpatient Medication Orders",
- "options": "Inpatient Medication Entry Detail",
- "reqd": 1
- },
- {
- "depends_on": "eval:doc.docstatus!==1",
- "fieldname": "get_medication_orders",
- "fieldtype": "Button",
- "label": "Get Pending Medication Orders",
- "print_hide": 1
- },
- {
- "fieldname": "assigned_to_practitioner",
- "fieldtype": "Link",
- "label": "Assigned To",
- "options": "User"
- },
- {
- "fieldname": "section_break_18",
- "fieldtype": "Section Break",
- "label": "Stock Details"
- },
- {
- "default": "1",
- "fieldname": "update_stock",
- "fieldtype": "Check",
- "label": "Update Stock"
- },
- {
- "fieldname": "from_time",
- "fieldtype": "Time",
- "label": "From Time"
- },
- {
- "fieldname": "to_time",
- "fieldtype": "Time",
- "label": "To Time"
- }
- ],
- "index_web_pages_for_search": 1,
- "is_submittable": 1,
- "links": [],
- "modified": "2021-01-11 12:37:46.749659",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Medication Entry",
- "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",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
deleted file mode 100644
index 3a299ed..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import flt, get_link_to_form, getdate, nowtime
-from erpnext.stock.utils import get_latest_stock_qty
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
-
-class InpatientMedicationEntry(Document):
- def validate(self):
- self.validate_medication_orders()
-
- @frappe.whitelist()
- def get_medication_orders(self):
- # pull inpatient medication orders based on selected filters
- orders = get_pending_medication_orders(self)
-
- if orders:
- self.add_mo_to_table(orders)
- return self
- else:
- self.set('medication_orders', [])
- frappe.msgprint(_('No pending medication orders found for selected criteria'))
-
- def add_mo_to_table(self, orders):
- # Add medication orders in the child table
- self.set('medication_orders', [])
-
- for data in orders:
- self.append('medication_orders', {
- 'patient': data.patient,
- 'patient_name': data.patient_name,
- 'inpatient_record': data.inpatient_record,
- 'service_unit': data.service_unit,
- 'datetime': "%s %s" % (data.date, data.time or "00:00:00"),
- 'drug_code': data.drug,
- 'drug_name': data.drug_name,
- 'dosage': data.dosage,
- 'dosage_form': data.dosage_form,
- 'against_imo': data.parent,
- 'against_imoe': data.name
- })
-
- def on_submit(self):
- self.validate_medication_orders()
- success_msg = ""
- if self.update_stock:
- stock_entry = self.process_stock()
- success_msg += _('Stock Entry {0} created and ').format(
- frappe.bold(get_link_to_form('Stock Entry', stock_entry)))
-
- self.update_medication_orders()
- success_msg += _('Inpatient Medication Orders updated successfully')
- frappe.msgprint(success_msg, title=_('Success'), indicator='green')
-
- def validate_medication_orders(self):
- for entry in self.medication_orders:
- docstatus, is_completed = frappe.db.get_value('Inpatient Medication Order Entry', entry.against_imoe,
- ['docstatus', 'is_completed'])
-
- if docstatus == 2:
- frappe.throw(_('Row {0}: Cannot create Inpatient Medication Entry against cancelled Inpatient Medication Order {1}').format(
- entry.idx, get_link_to_form(entry.against_imo)))
-
- if is_completed:
- frappe.throw(_('Row {0}: This Medication Order is already marked as completed').format(
- entry.idx))
-
- def on_cancel(self):
- self.cancel_stock_entries()
- self.update_medication_orders(on_cancel=True)
-
- def process_stock(self):
- allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
- if not allow_negative_stock:
- self.check_stock_qty()
-
- return self.make_stock_entry()
-
- def update_medication_orders(self, on_cancel=False):
- orders, order_entry_map = self.get_order_entry_map()
- # mark completion status
- is_completed = 1
- if on_cancel:
- is_completed = 0
-
- frappe.db.sql("""
- UPDATE `tabInpatient Medication Order Entry`
- SET is_completed = %(is_completed)s
- WHERE name IN %(orders)s
- """, {'orders': orders, 'is_completed': is_completed})
-
- # update status and completed orders count
- for order, count in order_entry_map.items():
- medication_order = frappe.get_doc('Inpatient Medication Order', order)
- completed_orders = flt(count)
- current_value = frappe.db.get_value('Inpatient Medication Order', order, 'completed_orders')
-
- if on_cancel:
- completed_orders = flt(current_value) - flt(count)
- else:
- completed_orders = flt(current_value) + flt(count)
-
- medication_order.db_set('completed_orders', completed_orders)
- medication_order.set_status()
-
- def get_order_entry_map(self):
- # for marking order completion status
- orders = []
- # orders mapped
- order_entry_map = dict()
-
- for entry in self.medication_orders:
- orders.append(entry.against_imoe)
- parent = entry.against_imo
- if not order_entry_map.get(parent):
- order_entry_map[parent] = 0
-
- order_entry_map[parent] += 1
-
- return orders, order_entry_map
-
- def check_stock_qty(self):
- drug_shortage = get_drug_shortage_map(self.medication_orders, self.warehouse)
-
- if drug_shortage:
- message = _('Quantity not available for the following items in warehouse {0}. ').format(frappe.bold(self.warehouse))
- message += _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.')
-
- formatted_item_rows = ''
-
- for drug, shortage_qty in drug_shortage.items():
- item_link = get_link_to_form('Item', drug)
- formatted_item_rows += """
- <td>{0}</td>
- <td>{1}</td>
- </tr>""".format(item_link, frappe.bold(shortage_qty))
-
- message += """
- <table class='table'>
- <thead>
- <th>{0}</th>
- <th>{1}</th>
- </thead>
- {2}
- </table>
- """.format(_('Drug Code'), _('Shortage Qty'), formatted_item_rows)
-
- frappe.throw(message, title=_('Insufficient Stock'), is_minimizable=True, wide=True)
-
- def make_stock_entry(self):
- stock_entry = frappe.new_doc('Stock Entry')
- stock_entry.purpose = 'Material Issue'
- stock_entry.set_stock_entry_type()
- stock_entry.from_warehouse = self.warehouse
- stock_entry.company = self.company
- stock_entry.inpatient_medication_entry = self.name
- cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
- expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
-
- for entry in self.medication_orders:
- se_child = stock_entry.append('items')
- se_child.item_code = entry.drug_code
- se_child.item_name = entry.drug_name
- se_child.uom = frappe.db.get_value('Item', entry.drug_code, 'stock_uom')
- se_child.stock_uom = se_child.uom
- se_child.qty = flt(entry.dosage)
- # in stock uom
- se_child.conversion_factor = 1
- se_child.cost_center = cost_center
- se_child.expense_account = expense_account
- # references
- se_child.patient = entry.patient
- se_child.inpatient_medication_entry_child = entry.name
-
- stock_entry.submit()
- return stock_entry.name
-
- def cancel_stock_entries(self):
- stock_entries = frappe.get_all('Stock Entry', {'inpatient_medication_entry': self.name})
- for entry in stock_entries:
- doc = frappe.get_doc('Stock Entry', entry.name)
- doc.cancel()
-
-
-def get_pending_medication_orders(entry):
- filters, values = get_filters(entry)
- to_remove = []
-
- data = frappe.db.sql("""
- SELECT
- ip.inpatient_record, ip.patient, ip.patient_name,
- entry.name, entry.parent, entry.drug, entry.drug_name,
- entry.dosage, entry.dosage_form, entry.date, entry.time, entry.instructions
- FROM
- `tabInpatient Medication Order` ip
- INNER JOIN
- `tabInpatient Medication Order Entry` entry
- ON
- ip.name = entry.parent
- WHERE
- ip.docstatus = 1 and
- ip.company = %(company)s and
- entry.is_completed = 0
- {0}
- ORDER BY
- entry.date, entry.time
- """.format(filters), values, as_dict=1)
-
- for doc in data:
- inpatient_record = doc.inpatient_record
- if inpatient_record:
- doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
-
- if entry.service_unit and doc.service_unit != entry.service_unit:
- to_remove.append(doc)
-
- for doc in to_remove:
- data.remove(doc)
-
- return data
-
-
-def get_filters(entry):
- filters = ''
- values = dict(company=entry.company)
- if entry.from_date:
- filters += ' and entry.date >= %(from_date)s'
- values['from_date'] = entry.from_date
-
- if entry.to_date:
- filters += ' and entry.date <= %(to_date)s'
- values['to_date'] = entry.to_date
-
- if entry.from_time:
- filters += ' and entry.time >= %(from_time)s'
- values['from_time'] = entry.from_time
-
- if entry.to_time:
- filters += ' and entry.time <= %(to_time)s'
- values['to_time'] = entry.to_time
-
- if entry.patient:
- filters += ' and ip.patient = %(patient)s'
- values['patient'] = entry.patient
-
- if entry.practitioner:
- filters += ' and ip.practitioner = %(practitioner)s'
- values['practitioner'] = entry.practitioner
-
- if entry.item_code:
- filters += ' and entry.drug = %(item_code)s'
- values['item_code'] = entry.item_code
-
- if entry.assigned_to_practitioner:
- filters += ' and ip._assign LIKE %(assigned_to)s'
- values['assigned_to'] = '%' + entry.assigned_to_practitioner + '%'
-
- return filters, values
-
-
-def get_current_healthcare_service_unit(inpatient_record):
- ip_record = frappe.get_doc('Inpatient Record', inpatient_record)
- if ip_record.status in ['Admitted', 'Discharge Scheduled'] and ip_record.inpatient_occupancies:
- return ip_record.inpatient_occupancies[-1].service_unit
- return
-
-
-def get_drug_shortage_map(medication_orders, warehouse):
- """
- Returns a dict like { drug_code: shortage_qty }
- """
- drug_requirement = dict()
- for d in medication_orders:
- if not drug_requirement.get(d.drug_code):
- drug_requirement[d.drug_code] = 0
- drug_requirement[d.drug_code] += flt(d.dosage)
-
- drug_shortage = dict()
- for drug, required_qty in drug_requirement.items():
- available_qty = get_latest_stock_qty(drug, warehouse)
- if flt(required_qty) > flt(available_qty):
- drug_shortage[drug] = flt(flt(required_qty) - flt(available_qty))
-
- return drug_shortage
-
-
-@frappe.whitelist()
-def make_difference_stock_entry(docname):
- doc = frappe.get_doc('Inpatient Medication Entry', docname)
- drug_shortage = get_drug_shortage_map(doc.medication_orders, doc.warehouse)
-
- if not drug_shortage:
- return None
-
- stock_entry = frappe.new_doc('Stock Entry')
- stock_entry.purpose = 'Material Transfer'
- stock_entry.set_stock_entry_type()
- stock_entry.to_warehouse = doc.warehouse
- stock_entry.company = doc.company
- cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
- expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
-
- for drug, shortage_qty in drug_shortage.items():
- se_child = stock_entry.append('items')
- se_child.item_code = drug
- se_child.item_name = frappe.db.get_value('Item', drug, 'stock_uom')
- se_child.uom = frappe.db.get_value('Item', drug, 'stock_uom')
- se_child.stock_uom = se_child.uom
- se_child.qty = flt(shortage_qty)
- se_child.t_warehouse = doc.warehouse
- # in stock uom
- se_child.conversion_factor = 1
- se_child.cost_center = cost_center
- se_child.expense_account = expense_account
-
- return stock_entry
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry_dashboard.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry_dashboard.py
deleted file mode 100644
index a4bec45..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry_dashboard.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'against_imoe',
- 'internal_links': {
- 'Inpatient Medication Order': ['medication_orders', 'against_imo']
- },
- 'transactions': [
- {
- 'label': _('Reference'),
- 'items': ['Inpatient Medication Order']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py
deleted file mode 100644
index 7cb5a48..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-from frappe.utils import add_days, getdate, now_datetime
-from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
-from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
-from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
-from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_drug_shortage_map, make_difference_stock_entry
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
-
-class TestInpatientMedicationEntry(unittest.TestCase):
- def setUp(self):
- frappe.db.sql("""delete from `tabInpatient Record`""")
- frappe.db.sql("""delete from `tabInpatient Medication Order`""")
- frappe.db.sql("""delete from `tabInpatient Medication Entry`""")
- self.patient = create_patient()
-
- # Admit
- ip_record = create_inpatient(self.patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save()
- ip_record.reload()
- service_unit = get_healthcare_service_unit()
- admit_patient(ip_record, service_unit, now_datetime())
- self.ip_record = ip_record
-
- def test_filters_for_fetching_pending_mo(self):
- ipmo = create_ipmo(self.patient)
- ipmo.submit()
- ipmo.reload()
-
- date = add_days(getdate(), -1)
- filters = frappe._dict(
- from_date=date,
- to_date=date,
- from_time='',
- to_time='',
- item_code='Dextromethorphan',
- patient=self.patient
- )
-
- ipme = create_ipme(filters, update_stock=0)
-
- # 3 dosages per day
- self.assertEqual(len(ipme.medication_orders), 3)
- self.assertEqual(getdate(ipme.medication_orders[0].datetime), date)
-
- def test_ipme_with_stock_update(self):
- ipmo = create_ipmo(self.patient)
- ipmo.submit()
- ipmo.reload()
-
- date = add_days(getdate(), -1)
- filters = frappe._dict(
- from_date=date,
- to_date=date,
- from_time='',
- to_time='',
- item_code='Dextromethorphan',
- patient=self.patient
- )
-
- make_stock_entry()
- ipme = create_ipme(filters, update_stock=1)
- ipme.submit()
- ipme.reload()
-
- # test order completed
- is_order_completed = frappe.db.get_value('Inpatient Medication Order Entry',
- ipme.medication_orders[0].against_imoe, 'is_completed')
- self.assertEqual(is_order_completed, 1)
-
- # test stock entry
- stock_entry = frappe.db.exists('Stock Entry', {'inpatient_medication_entry': ipme.name})
- self.assertTrue(stock_entry)
-
- # check references
- stock_entry = frappe.get_doc('Stock Entry', stock_entry)
- self.assertEqual(stock_entry.items[0].patient, self.patient)
- self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name)
-
- def test_drug_shortage_stock_entry(self):
- ipmo = create_ipmo(self.patient)
- ipmo.submit()
- ipmo.reload()
-
- date = add_days(getdate(), -1)
- filters = frappe._dict(
- from_date=date,
- to_date=date,
- from_time='',
- to_time='',
- item_code='Dextromethorphan',
- patient=self.patient
- )
-
- # check drug shortage
- ipme = create_ipme(filters, update_stock=1)
- ipme.warehouse = 'Finished Goods - _TC'
- ipme.save()
- drug_shortage = get_drug_shortage_map(ipme.medication_orders, ipme.warehouse)
- self.assertEqual(drug_shortage.get('Dextromethorphan'), 3)
-
- # check material transfer for drug shortage
- make_stock_entry()
- stock_entry = make_difference_stock_entry(ipme.name)
- self.assertEqual(stock_entry.items[0].item_code, 'Dextromethorphan')
- self.assertEqual(stock_entry.items[0].qty, 3)
- stock_entry.from_warehouse = 'Stores - _TC'
- stock_entry.submit()
-
- ipme.reload()
- ipme.submit()
-
- def tearDown(self):
- # cleanup - Discharge
- schedule_discharge(frappe.as_json({'patient': self.patient}))
- self.ip_record.reload()
- mark_invoiced_inpatient_occupancy(self.ip_record)
-
- self.ip_record.reload()
- discharge_patient(self.ip_record)
-
- for entry in frappe.get_all('Inpatient Medication Entry'):
- doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
- doc.cancel()
-
- for entry in frappe.get_all('Inpatient Medication Order'):
- doc = frappe.get_doc('Inpatient Medication Order', entry.name)
- doc.cancel()
-
-def make_stock_entry(warehouse=None):
- frappe.db.set_value('Company', '_Test Company', {
- 'stock_adjustment_account': 'Stock Adjustment - _TC',
- 'default_inventory_account': 'Stock In Hand - _TC'
- })
- stock_entry = frappe.new_doc('Stock Entry')
- stock_entry.stock_entry_type = 'Material Receipt'
- stock_entry.company = '_Test Company'
- stock_entry.to_warehouse = warehouse or 'Stores - _TC'
- expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company')
- se_child = stock_entry.append('items')
- se_child.item_code = 'Dextromethorphan'
- se_child.item_name = 'Dextromethorphan'
- se_child.uom = 'Nos'
- se_child.stock_uom = 'Nos'
- se_child.qty = 6
- se_child.t_warehouse = 'Stores - _TC'
- # in stock uom
- se_child.conversion_factor = 1.0
- se_child.expense_account = expense_account
- stock_entry.submit()
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry_detail/__init__.py b/erpnext/healthcare/doctype/inpatient_medication_entry_detail/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry_detail/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry_detail/inpatient_medication_entry_detail.json b/erpnext/healthcare/doctype/inpatient_medication_entry_detail/inpatient_medication_entry_detail.json
deleted file mode 100644
index e3d7212..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry_detail/inpatient_medication_entry_detail.json
+++ /dev/null
@@ -1,163 +0,0 @@
-{
- "actions": [],
- "creation": "2020-09-25 14:56:32.636569",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "patient",
- "patient_name",
- "inpatient_record",
- "column_break_4",
- "service_unit",
- "datetime",
- "medication_details_section",
- "drug_code",
- "drug_name",
- "dosage",
- "available_qty",
- "dosage_form",
- "column_break_10",
- "instructions",
- "references_section",
- "against_imo",
- "against_imoe"
- ],
- "fields": [
- {
- "columns": 2,
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "columns": 2,
- "fieldname": "drug_code",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Drug Code",
- "options": "Item",
- "reqd": 1
- },
- {
- "fetch_from": "drug_code.item_name",
- "fieldname": "drug_name",
- "fieldtype": "Data",
- "label": "Drug Name",
- "read_only": 1
- },
- {
- "columns": 1,
- "fieldname": "dosage",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Dosage",
- "reqd": 1
- },
- {
- "fieldname": "dosage_form",
- "fieldtype": "Link",
- "label": "Dosage Form",
- "options": "Dosage Form"
- },
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "references_section",
- "fieldtype": "Section Break",
- "label": "References"
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "medication_details_section",
- "fieldtype": "Section Break",
- "label": "Medication Details"
- },
- {
- "fieldname": "column_break_10",
- "fieldtype": "Column Break"
- },
- {
- "columns": 3,
- "fieldname": "datetime",
- "fieldtype": "Datetime",
- "in_list_view": 1,
- "label": "Datetime",
- "reqd": 1
- },
- {
- "fieldname": "instructions",
- "fieldtype": "Small Text",
- "label": "Instructions"
- },
- {
- "columns": 2,
- "fieldname": "service_unit",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Service Unit",
- "options": "Healthcare Service Unit",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "against_imo",
- "fieldtype": "Link",
- "label": "Against Inpatient Medication Order",
- "no_copy": 1,
- "options": "Inpatient Medication Order",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "against_imoe",
- "fieldtype": "Data",
- "label": "Against Inpatient Medication Order Entry",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "available_qty",
- "fieldtype": "Float",
- "hidden": 1,
- "label": "Available Qty",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2020-09-30 14:48:23.648223",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Medication Entry Detail",
- "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/healthcare/doctype/inpatient_medication_entry_detail/inpatient_medication_entry_detail.py b/erpnext/healthcare/doctype/inpatient_medication_entry_detail/inpatient_medication_entry_detail.py
deleted file mode 100644
index 644898d..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_entry_detail/inpatient_medication_entry_detail.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class InpatientMedicationEntryDetail(Document):
- pass
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/__init__.py b/erpnext/healthcare/doctype/inpatient_medication_order/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js b/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js
deleted file mode 100644
index 690e2e7..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Inpatient Medication Order', {
- refresh: function(frm) {
- if (frm.doc.docstatus === 1) {
- frm.trigger("show_progress");
- }
-
- frm.events.show_medication_order_button(frm);
-
- frm.set_query('patient', () => {
- return {
- filters: {
- 'inpatient_record': ['!=', ''],
- 'inpatient_status': 'Admitted'
- }
- };
- });
- },
-
- show_medication_order_button: function(frm) {
- frm.fields_dict['medication_orders'].grid.wrapper.find('.grid-add-row').hide();
- frm.fields_dict['medication_orders'].grid.add_custom_button(__('Add Medication Orders'), () => {
- let d = new frappe.ui.Dialog({
- title: __('Add Medication Orders'),
- fields: [
- {
- fieldname: 'drug_code',
- label: __('Drug'),
- fieldtype: 'Link',
- options: 'Item',
- reqd: 1,
- "get_query": function () {
- return {
- filters: {'is_stock_item': 1}
- };
- }
- },
- {
- fieldname: 'dosage',
- label: __('Dosage'),
- fieldtype: 'Link',
- options: 'Prescription Dosage',
- reqd: 1
- },
- {
- fieldname: 'period',
- label: __('Period'),
- fieldtype: 'Link',
- options: 'Prescription Duration',
- reqd: 1
- },
- {
- fieldname: 'dosage_form',
- label: __('Dosage Form'),
- fieldtype: 'Link',
- options: 'Dosage Form',
- reqd: 1
- }
- ],
- primary_action_label: __('Add'),
- primary_action: () => {
- let values = d.get_values();
- if (values) {
- frm.call({
- doc: frm.doc,
- method: 'add_order_entries',
- args: {
- order: values
- },
- freeze: true,
- freeze_message: __('Adding Order Entries'),
- callback: function() {
- frm.refresh_field('medication_orders');
- }
- });
- }
- },
- });
- d.show();
- });
- },
-
- show_progress: function(frm) {
- let bars = [];
- let message = '';
-
- // completed sessions
- let title = __('{0} medication orders completed', [frm.doc.completed_orders]);
- if (frm.doc.completed_orders === 1) {
- title = __('{0} medication order completed', [frm.doc.completed_orders]);
- }
- title += __(' out of {0}', [frm.doc.total_orders]);
-
- bars.push({
- 'title': title,
- 'width': (frm.doc.completed_orders / frm.doc.total_orders * 100) + '%',
- 'progress_class': 'progress-bar-success'
- });
- if (bars[0].width == '0%') {
- bars[0].width = '0.5%';
- }
- message = title;
- frm.dashboard.add_progress(__('Status'), bars, message);
- }
-});
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.json b/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.json
deleted file mode 100644
index e31d2e3..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.json
+++ /dev/null
@@ -1,196 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2020-09-14 18:33:56.715736",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "patient_details_section",
- "naming_series",
- "patient_encounter",
- "patient",
- "patient_name",
- "patient_age",
- "inpatient_record",
- "column_break_6",
- "company",
- "status",
- "practitioner",
- "start_date",
- "end_date",
- "medication_orders_section",
- "medication_orders",
- "section_break_16",
- "total_orders",
- "column_break_18",
- "completed_orders",
- "amended_from"
- ],
- "fields": [
- {
- "fieldname": "patient_details_section",
- "fieldtype": "Section Break",
- "label": "Patient Details"
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Naming Series",
- "options": "HLC-IMO-.YYYY.-"
- },
- {
- "fieldname": "patient_encounter",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient Encounter",
- "options": "Patient Encounter"
- },
- {
- "fetch_from": "patient_encounter.patient",
- "fieldname": "patient",
- "fieldtype": "Link",
- "label": "Patient",
- "options": "Patient",
- "read_only_depends_on": "patient_encounter",
- "reqd": 1
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "label": "Patient Age",
- "read_only": 1
- },
- {
- "fieldname": "column_break_6",
- "fieldtype": "Column Break"
- },
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fetch_from": "patient_encounter.practitioner",
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "read_only_depends_on": "patient_encounter"
- },
- {
- "fieldname": "start_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Start Date",
- "reqd": 1
- },
- {
- "fieldname": "end_date",
- "fieldtype": "Date",
- "label": "End Date",
- "read_only": 1
- },
- {
- "depends_on": "eval: doc.patient && doc.start_date",
- "fieldname": "medication_orders_section",
- "fieldtype": "Section Break",
- "label": "Medication Orders"
- },
- {
- "fieldname": "medication_orders",
- "fieldtype": "Table",
- "label": "Medication Orders",
- "options": "Inpatient Medication Order Entry"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
- "reqd": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Inpatient Medication Order",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Status",
- "options": "\nDraft\nSubmitted\nPending\nIn Process\nCompleted\nCancelled",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_16",
- "fieldtype": "Section Break",
- "label": "Other Details"
- },
- {
- "fieldname": "total_orders",
- "fieldtype": "Float",
- "label": "Total Orders",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "column_break_18",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "completed_orders",
- "fieldtype": "Float",
- "label": "Completed Orders",
- "no_copy": 1,
- "read_only": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "is_submittable": 1,
- "links": [],
- "modified": "2020-09-30 21:53:27.128591",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Medication Order",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "search_fields": "patient_encounter, patient",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.py b/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.py
deleted file mode 100644
index b379e98..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cstr
-from erpnext.healthcare.doctype.patient_encounter.patient_encounter import get_prescription_dates
-
-class InpatientMedicationOrder(Document):
- def validate(self):
- self.validate_inpatient()
- self.validate_duplicate()
- self.set_total_orders()
- self.set_status()
-
- def on_submit(self):
- self.validate_inpatient()
- self.set_status()
-
- def on_cancel(self):
- self.set_status()
-
- def validate_inpatient(self):
- if not self.inpatient_record:
- frappe.throw(_('No Inpatient Record found against patient {0}').format(self.patient))
-
- def validate_duplicate(self):
- existing_mo = frappe.db.exists('Inpatient Medication Order', {
- 'patient_encounter': self.patient_encounter,
- 'docstatus': ('!=', 2),
- 'name': ('!=', self.name)
- })
- if existing_mo:
- frappe.throw(_('An Inpatient Medication Order {0} against Patient Encounter {1} already exists.').format(
- existing_mo, self.patient_encounter), frappe.DuplicateEntryError)
-
- def set_total_orders(self):
- self.db_set('total_orders', len(self.medication_orders))
-
- def set_status(self):
- status = {
- "0": "Draft",
- "1": "Submitted",
- "2": "Cancelled"
- }[cstr(self.docstatus or 0)]
-
- if self.docstatus == 1:
- if not self.completed_orders:
- status = 'Pending'
- elif self.completed_orders < self.total_orders:
- status = 'In Process'
- else:
- status = 'Completed'
-
- self.db_set('status', status)
-
- @frappe.whitelist()
- def add_order_entries(self, order):
- if order.get('drug_code'):
- dosage = frappe.get_doc('Prescription Dosage', order.get('dosage'))
- dates = get_prescription_dates(order.get('period'), self.start_date)
- for date in dates:
- for dose in dosage.dosage_strength:
- entry = self.append('medication_orders')
- entry.drug = order.get('drug_code')
- entry.drug_name = frappe.db.get_value('Item', order.get('drug_code'), 'item_name')
- entry.dosage = dose.strength
- entry.dosage_form = order.get('dosage_form')
- entry.date = date
- entry.time = dose.strength_time
- self.end_date = dates[-1]
- return
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order_list.js b/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order_list.js
deleted file mode 100644
index 1c31876..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order_list.js
+++ /dev/null
@@ -1,16 +0,0 @@
-frappe.listview_settings['Inpatient Medication Order'] = {
- add_fields: ["status"],
- filters: [["status", "!=", "Cancelled"]],
- get_indicator: function(doc) {
- if (doc.status === "Pending") {
- return [__("Pending"), "orange", "status,=,Pending"];
-
- } else if (doc.status === "In Process") {
- return [__("In Process"), "blue", "status,=,In Process"];
-
- } else if (doc.status === "Completed") {
- return [__("Completed"), "green", "status,=,Completed"];
-
- }
- }
-};
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py b/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py
deleted file mode 100644
index 21776d2..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-from frappe.utils import add_days, getdate, now_datetime
-from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
-from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
-
-class TestInpatientMedicationOrder(unittest.TestCase):
- def setUp(self):
- frappe.db.sql("""delete from `tabInpatient Record`""")
- self.patient = create_patient()
-
- # Admit
- ip_record = create_inpatient(self.patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save()
- ip_record.reload()
- service_unit = get_healthcare_service_unit()
- admit_patient(ip_record, service_unit, now_datetime())
- self.ip_record = ip_record
-
- def test_order_creation(self):
- ipmo = create_ipmo(self.patient)
- ipmo.submit()
- ipmo.reload()
-
- # 3 dosages per day for 2 days
- self.assertEqual(len(ipmo.medication_orders), 6)
- self.assertEqual(ipmo.medication_orders[0].date, add_days(getdate(), -1))
-
- prescription_dosage = frappe.get_doc('Prescription Dosage', '1-1-1')
- for i in range(len(prescription_dosage.dosage_strength)):
- self.assertEqual(ipmo.medication_orders[i].time, prescription_dosage.dosage_strength[i].strength_time)
-
- self.assertEqual(ipmo.medication_orders[3].date, getdate())
-
- def test_inpatient_validation(self):
- # Discharge
- schedule_discharge(frappe.as_json({'patient': self.patient}))
-
- self.ip_record.reload()
- mark_invoiced_inpatient_occupancy(self.ip_record)
-
- self.ip_record.reload()
- discharge_patient(self.ip_record)
-
- ipmo = create_ipmo(self.patient)
- # inpatient validation
- self.assertRaises(frappe.ValidationError, ipmo.insert)
-
- def test_status(self):
- ipmo = create_ipmo(self.patient)
- ipmo.submit()
- ipmo.reload()
-
- self.assertEqual(ipmo.status, 'Pending')
-
- filters = frappe._dict(from_date=add_days(getdate(), -1), to_date=add_days(getdate(), -1), from_time='', to_time='')
- ipme = create_ipme(filters)
- ipme.submit()
- ipmo.reload()
- self.assertEqual(ipmo.status, 'In Process')
-
- filters = frappe._dict(from_date=getdate(), to_date=getdate(), from_time='', to_time='')
- ipme = create_ipme(filters)
- ipme.submit()
- ipmo.reload()
- self.assertEqual(ipmo.status, 'Completed')
-
- def tearDown(self):
- if frappe.db.get_value('Patient', self.patient, 'inpatient_record'):
- # cleanup - Discharge
- schedule_discharge(frappe.as_json({'patient': self.patient}))
- self.ip_record.reload()
- mark_invoiced_inpatient_occupancy(self.ip_record)
-
- self.ip_record.reload()
- discharge_patient(self.ip_record)
-
- for doctype in ["Inpatient Medication Entry", "Inpatient Medication Order"]:
- frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
-
-def create_dosage_form():
- if not frappe.db.exists('Dosage Form', 'Tablet'):
- frappe.get_doc({
- 'doctype': 'Dosage Form',
- 'dosage_form': 'Tablet'
- }).insert()
-
-def create_drug(item=None):
- if not item:
- item = 'Dextromethorphan'
- drug = frappe.db.exists('Item', {'item_code': 'Dextromethorphan'})
- if not drug:
- drug = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': 'Dextromethorphan',
- 'item_name': 'Dextromethorphan',
- 'item_group': 'Products',
- 'stock_uom': 'Nos',
- 'is_stock_item': 1,
- 'valuation_rate': 50,
- 'opening_stock': 20
- }).insert()
-
-def get_orders():
- create_dosage_form()
- create_drug()
- return {
- 'drug_code': 'Dextromethorphan',
- 'drug_name': 'Dextromethorphan',
- 'dosage': '1-1-1',
- 'dosage_form': 'Tablet',
- 'period': '2 Day'
- }
-
-def create_ipmo(patient):
- orders = get_orders()
- ipmo = frappe.new_doc('Inpatient Medication Order')
- ipmo.patient = patient
- ipmo.company = '_Test Company'
- ipmo.start_date = add_days(getdate(), -1)
- ipmo.add_order_entries(orders)
-
- return ipmo
-
-def create_ipme(filters, update_stock=0):
- ipme = frappe.new_doc('Inpatient Medication Entry')
- ipme.company = '_Test Company'
- ipme.posting_date = getdate()
- ipme.update_stock = update_stock
- if update_stock:
- ipme.warehouse = 'Stores - _TC'
- for key, value in filters.items():
- ipme.set(key, value)
- ipme = ipme.get_medication_orders()
-
- return ipme
-
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order_entry/__init__.py b/erpnext/healthcare/doctype/inpatient_medication_order_entry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order_entry/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order_entry/inpatient_medication_order_entry.json b/erpnext/healthcare/doctype/inpatient_medication_order_entry/inpatient_medication_order_entry.json
deleted file mode 100644
index 72999a9..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order_entry/inpatient_medication_order_entry.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "actions": [],
- "creation": "2020-09-14 21:51:30.259164",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "drug",
- "drug_name",
- "dosage",
- "dosage_form",
- "instructions",
- "column_break_4",
- "date",
- "time",
- "is_completed"
- ],
- "fields": [
- {
- "fieldname": "drug",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Drug",
- "options": "Item",
- "reqd": 1
- },
- {
- "fetch_from": "drug.item_name",
- "fieldname": "drug_name",
- "fieldtype": "Data",
- "label": "Drug Name",
- "read_only": 1
- },
- {
- "fieldname": "dosage",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Dosage",
- "reqd": 1
- },
- {
- "fieldname": "dosage_form",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Dosage Form",
- "options": "Dosage Form",
- "reqd": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Date",
- "reqd": 1
- },
- {
- "fieldname": "time",
- "fieldtype": "Time",
- "in_list_view": 1,
- "label": "Time",
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "is_completed",
- "fieldtype": "Check",
- "label": "Is Order Completed",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "instructions",
- "fieldtype": "Small Text",
- "label": "Instructions"
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2020-09-30 14:03:26.755925",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Medication Order Entry",
- "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/healthcare/doctype/inpatient_medication_order_entry/inpatient_medication_order_entry.py b/erpnext/healthcare/doctype/inpatient_medication_order_entry/inpatient_medication_order_entry.py
deleted file mode 100644
index ebfe366..0000000
--- a/erpnext/healthcare/doctype/inpatient_medication_order_entry/inpatient_medication_order_entry.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class InpatientMedicationOrderEntry(Document):
- pass
diff --git a/erpnext/healthcare/doctype/inpatient_occupancy/__init__.py b/erpnext/healthcare/doctype/inpatient_occupancy/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/inpatient_occupancy/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json
deleted file mode 100644
index 3fa98b6..0000000
--- a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "actions": [],
- "creation": "2018-07-12 12:07:36.932333",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "service_unit",
- "check_in",
- "left",
- "check_out",
- "invoiced"
- ],
- "fields": [
- {
- "fieldname": "service_unit",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Healthcare Service Unit",
- "options": "Healthcare Service Unit",
- "reqd": 1
- },
- {
- "fieldname": "check_in",
- "fieldtype": "Datetime",
- "in_list_view": 1,
- "label": "Check In"
- },
- {
- "default": "0",
- "fieldname": "left",
- "fieldtype": "Check",
- "label": "Left",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "check_out",
- "fieldtype": "Datetime",
- "label": "Check Out"
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "read_only": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-03-18 15:08:54.634132",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Occupancy",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.py b/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.py
deleted file mode 100644
index 52de25b..0000000
--- a/erpnext/healthcare/doctype/inpatient_occupancy/inpatient_occupancy.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class InpatientOccupancy(Document):
- pass
diff --git a/erpnext/healthcare/doctype/inpatient_record/__init__.py b/erpnext/healthcare/doctype/inpatient_record/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js
deleted file mode 100644
index 60f0f9d..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Inpatient Record', {
- setup: function(frm) {
- frm.get_field('drug_prescription').grid.editable_fields = [
- {fieldname: 'drug_code', columns: 2},
- {fieldname: 'drug_name', columns: 2},
- {fieldname: 'dosage', columns: 2},
- {fieldname: 'period', columns: 2}
- ];
- },
- refresh: function(frm) {
- if (!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) {
- frm.enable_save();
- } else {
- frm.disable_save();
- }
-
- if (!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') {
- frm.add_custom_button(__('Admit'), function() {
- admit_patient_dialog(frm);
- } );
- }
-
- if (!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') {
- frm.add_custom_button(__('Discharge'), function() {
- discharge_patient(frm);
- } );
- }
- if (!frm.doc.__islocal && frm.doc.status != 'Admitted') {
- frm.disable_save();
- frm.set_df_property('btn_transfer', 'hidden', 1);
- } else {
- frm.set_df_property('btn_transfer', 'hidden', 0);
- }
- },
- btn_transfer: function(frm) {
- transfer_patient_dialog(frm);
- }
-});
-
-let discharge_patient = function(frm) {
- frappe.call({
- doc: frm.doc,
- method: 'discharge',
- callback: function(data) {
- if (!data.exc) {
- frm.reload_doc();
- }
- },
- freeze: true,
- freeze_message: __('Processing Inpatient Discharge')
- });
-};
-
-let admit_patient_dialog = function(frm) {
- let dialog = new frappe.ui.Dialog({
- title: 'Admit Patient',
- width: 100,
- fields: [
- {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type',
- options: 'Healthcare Service Unit Type', default: frm.doc.admission_service_unit_type
- },
- {fieldtype: 'Link', label: 'Service Unit', fieldname: 'service_unit',
- options: 'Healthcare Service Unit', reqd: 1
- },
- {fieldtype: 'Datetime', label: 'Admission Datetime', fieldname: 'check_in',
- reqd: 1, default: frappe.datetime.now_datetime()
- },
- {fieldtype: 'Date', label: 'Expected Discharge', fieldname: 'expected_discharge',
- default: frm.doc.expected_length_of_stay ? frappe.datetime.add_days(frappe.datetime.now_datetime(), frm.doc.expected_length_of_stay) : ''
- }
- ],
- primary_action_label: __('Admit'),
- primary_action : function(){
- let service_unit = dialog.get_value('service_unit');
- let check_in = dialog.get_value('check_in');
- let expected_discharge = null;
- if (dialog.get_value('expected_discharge')) {
- expected_discharge = dialog.get_value('expected_discharge');
- }
- if (!service_unit && !check_in) {
- return;
- }
- frappe.call({
- doc: frm.doc,
- method: 'admit',
- args:{
- 'service_unit': service_unit,
- 'check_in': check_in,
- 'expected_discharge': expected_discharge
- },
- callback: function(data) {
- if (!data.exc) {
- frm.reload_doc();
- }
- },
- freeze: true,
- freeze_message: __('Processing Patient Admission')
- });
- frm.refresh_fields();
- dialog.hide();
- }
- });
-
- dialog.fields_dict['service_unit_type'].get_query = function() {
- return {
- filters: {
- 'inpatient_occupancy': 1,
- 'allow_appointments': 0
- }
- };
- };
- dialog.fields_dict['service_unit'].get_query = function() {
- return {
- filters: {
- 'is_group': 0,
- 'company': frm.doc.company,
- 'service_unit_type': dialog.get_value('service_unit_type'),
- 'occupancy_status' : 'Vacant'
- }
- };
- };
-
- dialog.show();
-};
-
-let transfer_patient_dialog = function(frm) {
- let dialog = new frappe.ui.Dialog({
- title: 'Transfer Patient',
- width: 100,
- fields: [
- {fieldtype: 'Link', label: 'Leave From', fieldname: 'leave_from', options: 'Healthcare Service Unit', reqd: 1, read_only:1},
- {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'},
- {fieldtype: 'Link', label: 'Transfer To', fieldname: 'service_unit', options: 'Healthcare Service Unit', reqd: 1},
- {fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1, default: frappe.datetime.now_datetime()}
- ],
- primary_action_label: __('Transfer'),
- primary_action : function() {
- let service_unit = null;
- let check_in = dialog.get_value('check_in');
- let leave_from = null;
- if(dialog.get_value('leave_from')){
- leave_from = dialog.get_value('leave_from');
- }
- if(dialog.get_value('service_unit')){
- service_unit = dialog.get_value('service_unit');
- }
- if(check_in > frappe.datetime.now_datetime()){
- frappe.msgprint({
- title: __('Not Allowed'),
- message: __('Check-in time cannot be greater than the current time'),
- indicator: 'red'
- });
- return;
- }
- frappe.call({
- doc: frm.doc,
- method: 'transfer',
- args:{
- 'service_unit': service_unit,
- 'check_in': check_in,
- 'leave_from': leave_from
- },
- callback: function(data) {
- if (!data.exc) {
- frm.reload_doc();
- }
- },
- freeze: true,
- freeze_message: __('Process Transfer')
- });
- frm.refresh_fields();
- dialog.hide();
- }
- });
-
- dialog.fields_dict['leave_from'].get_query = function(){
- return {
- query : 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.get_leave_from',
- filters: {docname:frm.doc.name}
- };
- };
- dialog.fields_dict['service_unit_type'].get_query = function(){
- return {
- filters: {
- 'inpatient_occupancy': 1,
- 'allow_appointments': 0
- }
- };
- };
- dialog.fields_dict['service_unit'].get_query = function(){
- return {
- filters: {
- 'is_group': 0,
- 'service_unit_type': dialog.get_value('service_unit_type'),
- 'occupancy_status' : 'Vacant'
- }
- };
- };
-
- dialog.show();
-
- let not_left_service_unit = null;
- for (let inpatient_occupancy in frm.doc.inpatient_occupancies) {
- if (frm.doc.inpatient_occupancies[inpatient_occupancy].left != 1) {
- not_left_service_unit = frm.doc.inpatient_occupancies[inpatient_occupancy].service_unit;
- }
- }
- dialog.set_values({
- 'leave_from': not_left_service_unit
- });
-};
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
deleted file mode 100644
index 0e1c2ba..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
+++ /dev/null
@@ -1,507 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2018-07-11 17:48:51.404139",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "section_break_1",
- "naming_series",
- "patient",
- "patient_name",
- "gender",
- "blood_group",
- "dob",
- "mobile",
- "email",
- "phone",
- "column_break_8",
- "company",
- "status",
- "scheduled_date",
- "admitted_datetime",
- "expected_discharge",
- "references",
- "admission_encounter",
- "admission_practitioner",
- "medical_department",
- "admission_ordered_for",
- "expected_length_of_stay",
- "admission_service_unit_type",
- "cb_admission",
- "primary_practitioner",
- "secondary_practitioner",
- "admission_instruction",
- "encounter_details_section",
- "chief_complaint",
- "column_break_29",
- "diagnosis",
- "medication_section",
- "drug_prescription",
- "investigations_section",
- "lab_test_prescription",
- "procedures_section",
- "procedure_prescription",
- "rehabilitation_section",
- "therapy_plan",
- "therapies",
- "sb_inpatient_occupancy",
- "inpatient_occupancies",
- "btn_transfer",
- "sb_discharge_details",
- "discharge_ordered_date",
- "discharge_practitioner",
- "discharge_encounter",
- "discharge_datetime",
- "cb_discharge",
- "discharge_instructions",
- "followup_date",
- "sb_discharge_note",
- "discharge_note"
- ],
- "fields": [
- {
- "fieldname": "section_break_1",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "hidden": 1,
- "label": "Series",
- "options": "HLC-INP-.YYYY.-"
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fetch_from": "patient.sex",
- "fieldname": "gender",
- "fieldtype": "Link",
- "label": "Gender",
- "options": "Gender",
- "read_only": 1
- },
- {
- "fetch_from": "patient.blood_group",
- "fieldname": "blood_group",
- "fieldtype": "Select",
- "label": "Blood Group",
- "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative",
- "read_only": 1
- },
- {
- "fetch_from": "patient.dob",
- "fieldname": "dob",
- "fieldtype": "Date",
- "label": "Date of birth",
- "read_only": 1
- },
- {
- "fetch_from": "patient.mobile",
- "fieldname": "mobile",
- "fieldtype": "Data",
- "label": "Mobile",
- "read_only": 1
- },
- {
- "fetch_from": "patient.email",
- "fieldname": "email",
- "fieldtype": "Data",
- "label": "Email",
- "options": "Email",
- "read_only": 1
- },
- {
- "fetch_from": "patient.phone",
- "fieldname": "phone",
- "fieldtype": "Data",
- "label": "Phone",
- "read_only": 1
- },
- {
- "fieldname": "medical_department",
- "fieldtype": "Link",
- "label": "Medical Department",
- "options": "Medical Department",
- "set_only_once": 1
- },
- {
- "fieldname": "primary_practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner (Primary)",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "secondary_practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner (Secondary)",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "column_break_8",
- "fieldtype": "Column Break"
- },
- {
- "default": "Admission Scheduled",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Status",
- "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged",
- "read_only": 1
- },
- {
- "default": "Today",
- "fieldname": "scheduled_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Admission Schedule Date",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "admission_ordered_for",
- "fieldtype": "Date",
- "label": "Admission Ordered For",
- "read_only": 1
- },
- {
- "fieldname": "admitted_datetime",
- "fieldtype": "Datetime",
- "in_list_view": 1,
- "label": "Admitted Datetime",
- "permlevel": 2
- },
- {
- "depends_on": "eval:(doc.expected_length_of_stay > 0)",
- "fieldname": "expected_length_of_stay",
- "fieldtype": "Int",
- "label": "Expected Length of Stay",
- "set_only_once": 1
- },
- {
- "fieldname": "expected_discharge",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Expected Discharge",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "references",
- "fieldtype": "Section Break",
- "label": "Admission Order Details"
- },
- {
- "fieldname": "cb_admission",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "admission_practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "read_only": 1
- },
- {
- "fieldname": "admission_encounter",
- "fieldtype": "Link",
- "label": "Patient Encounter",
- "options": "Patient Encounter",
- "read_only": 1
- },
- {
- "fieldname": "chief_complaint",
- "fieldtype": "Table MultiSelect",
- "label": "Chief Complaint",
- "options": "Patient Encounter Symptom",
- "permlevel": 1
- },
- {
- "fieldname": "admission_instruction",
- "fieldtype": "Small Text",
- "label": "Admission Instruction",
- "set_only_once": 1
- },
- {
- "fieldname": "cb_discharge",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "discharge_practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "read_only": 1
- },
- {
- "fieldname": "discharge_encounter",
- "fieldtype": "Link",
- "label": "Patient Encounter",
- "options": "Patient Encounter",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "medication_section",
- "fieldtype": "Section Break",
- "label": "Medications",
- "permlevel": 1
- },
- {
- "fieldname": "drug_prescription",
- "fieldtype": "Table",
- "options": "Drug Prescription",
- "permlevel": 1
- },
- {
- "collapsible": 1,
- "fieldname": "investigations_section",
- "fieldtype": "Section Break",
- "label": "Investigations",
- "permlevel": 1
- },
- {
- "fieldname": "lab_test_prescription",
- "fieldtype": "Table",
- "options": "Lab Prescription",
- "permlevel": 1
- },
- {
- "collapsible": 1,
- "fieldname": "procedures_section",
- "fieldtype": "Section Break",
- "label": "Procedures",
- "permlevel": 1
- },
- {
- "fieldname": "procedure_prescription",
- "fieldtype": "Table",
- "options": "Procedure Prescription",
- "permlevel": 1
- },
- {
- "depends_on": "eval:(doc.status != \"Admission Scheduled\")",
- "fieldname": "sb_inpatient_occupancy",
- "fieldtype": "Section Break",
- "label": "Inpatient Occupancy"
- },
- {
- "fieldname": "admission_service_unit_type",
- "fieldtype": "Link",
- "label": "Admission Service Unit Type",
- "options": "Healthcare Service Unit Type",
- "read_only": 1
- },
- {
- "fieldname": "inpatient_occupancies",
- "fieldtype": "Table",
- "options": "Inpatient Occupancy",
- "permlevel": 2
- },
- {
- "fieldname": "btn_transfer",
- "fieldtype": "Button",
- "label": "Transfer"
- },
- {
- "depends_on": "eval:(doc.status == \"Discharge Scheduled\" || doc.status == \"Discharged\")",
- "fieldname": "sb_discharge_note",
- "fieldtype": "Section Break",
- "label": "Discharge Notes"
- },
- {
- "fieldname": "discharge_note",
- "fieldtype": "Text Editor",
- "permlevel": 1
- },
- {
- "fetch_from": "admission_encounter.company",
- "fieldname": "company",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "eval:(doc.status == \"Admitted\")",
- "fieldname": "encounter_details_section",
- "fieldtype": "Section Break",
- "label": "Encounter Impression",
- "permlevel": 1
- },
- {
- "fieldname": "column_break_29",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "diagnosis",
- "fieldtype": "Table MultiSelect",
- "label": "Diagnosis",
- "options": "Patient Encounter Diagnosis",
- "permlevel": 1
- },
- {
- "fieldname": "followup_date",
- "fieldtype": "Date",
- "label": "Follow Up Date"
- },
- {
- "collapsible": 1,
- "depends_on": "eval:(doc.status == \"Discharge Scheduled\" || doc.status == \"Discharged\")",
- "fieldname": "sb_discharge_details",
- "fieldtype": "Section Break",
- "label": "Discharge Detials"
- },
- {
- "fieldname": "discharge_instructions",
- "fieldtype": "Small Text",
- "label": "Discharge Instructions"
- },
- {
- "fieldname": "discharge_ordered_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Discharge Ordered Date",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "rehabilitation_section",
- "fieldtype": "Section Break",
- "label": "Rehabilitation",
- "permlevel": 1
- },
- {
- "fieldname": "therapy_plan",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Therapy Plan",
- "options": "Therapy Plan",
- "permlevel": 1,
- "read_only": 1
- },
- {
- "fieldname": "therapies",
- "fieldtype": "Table",
- "options": "Therapy Plan Detail",
- "permlevel": 1
- },
- {
- "fieldname": "discharge_datetime",
- "fieldtype": "Datetime",
- "label": "Discharge Date",
- "permlevel": 2
- }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-03-18 15:59:17.318988",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Record",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1,
- "write": 1
- },
- {
- "permlevel": 1,
- "read": 1,
- "role": "Physician",
- "write": 1
- },
- {
- "permlevel": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User"
- },
- {
- "email": 1,
- "export": 1,
- "permlevel": 2,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "permlevel": 2,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1
- },
- {
- "email": 1,
- "export": 1,
- "permlevel": 2,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
deleted file mode 100644
index f4d1eaf..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
+++ /dev/null
@@ -1,287 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe, json
-from frappe import _
-from frappe.utils import today, now_datetime, getdate, get_datetime, get_link_to_form
-from frappe.model.document import Document
-from frappe.desk.reportview import get_match_cond
-
-class InpatientRecord(Document):
- def after_insert(self):
- frappe.db.set_value('Patient', self.patient, 'inpatient_record', self.name)
- frappe.db.set_value('Patient', self.patient, 'inpatient_status', self.status)
-
- if self.admission_encounter: # Update encounter
- frappe.db.set_value('Patient Encounter', self.admission_encounter, 'inpatient_record', self.name)
- frappe.db.set_value('Patient Encounter', self.admission_encounter, 'inpatient_status', self.status)
-
- def validate(self):
- self.validate_dates()
- self.validate_already_scheduled_or_admitted()
- if self.status == "Discharged":
- frappe.db.set_value("Patient", self.patient, "inpatient_status", None)
- frappe.db.set_value("Patient", self.patient, "inpatient_record", None)
-
- def validate_dates(self):
- if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \
- (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)):
- frappe.throw(_('Expected and Discharge dates cannot be less than Admission Schedule date'))
-
- for entry in self.inpatient_occupancies:
- if entry.check_in and entry.check_out and \
- get_datetime(entry.check_in) > get_datetime(entry.check_out):
- frappe.throw(_('Row #{0}: Check Out datetime cannot be less than Check In datetime').format(entry.idx))
-
- def validate_already_scheduled_or_admitted(self):
- query = """
- select name, status
- from `tabInpatient Record`
- where (status = 'Admitted' or status = 'Admission Scheduled')
- and name != %(name)s and patient = %(patient)s
- """
-
- ip_record = frappe.db.sql(query,{
- "name": self.name,
- "patient": self.patient
- }, as_dict = 1)
-
- if ip_record:
- msg = _(("Already {0} Patient {1} with Inpatient Record ").format(ip_record[0].status, self.patient) \
- + """ <b><a href="/app/Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
- frappe.throw(msg)
-
- @frappe.whitelist()
- def admit(self, service_unit, check_in, expected_discharge=None):
- admit_patient(self, service_unit, check_in, expected_discharge)
-
- @frappe.whitelist()
- def discharge(self):
- discharge_patient(self)
-
- @frappe.whitelist()
- def transfer(self, service_unit, check_in, leave_from):
- if leave_from:
- patient_leave_service_unit(self, check_in, leave_from)
- if service_unit:
- transfer_patient(self, service_unit, check_in)
-
-
-@frappe.whitelist()
-def schedule_inpatient(args):
- admission_order = json.loads(args) # admission order via Encounter
- if not admission_order or not admission_order['patient'] or not admission_order['admission_encounter']:
- frappe.throw(_('Missing required details, did not create Inpatient Record'))
-
- inpatient_record = frappe.new_doc('Inpatient Record')
-
- # Admission order details
- set_details_from_ip_order(inpatient_record, admission_order)
-
- # Patient details
- patient = frappe.get_doc('Patient', admission_order['patient'])
- inpatient_record.patient = patient.name
- inpatient_record.patient_name = patient.patient_name
- inpatient_record.gender = patient.sex
- inpatient_record.blood_group = patient.blood_group
- inpatient_record.dob = patient.dob
- inpatient_record.mobile = patient.mobile
- inpatient_record.email = patient.email
- inpatient_record.phone = patient.phone
- inpatient_record.scheduled_date = today()
-
- # Set encounter detials
- encounter = frappe.get_doc('Patient Encounter', admission_order['admission_encounter'])
- if encounter and encounter.symptoms: # Symptoms
- set_ip_child_records(inpatient_record, 'chief_complaint', encounter.symptoms)
-
- if encounter and encounter.diagnosis: # Diagnosis
- set_ip_child_records(inpatient_record, 'diagnosis', encounter.diagnosis)
-
- if encounter and encounter.drug_prescription: # Medication
- set_ip_child_records(inpatient_record, 'drug_prescription', encounter.drug_prescription)
-
- if encounter and encounter.lab_test_prescription: # Lab Tests
- set_ip_child_records(inpatient_record, 'lab_test_prescription', encounter.lab_test_prescription)
-
- if encounter and encounter.procedure_prescription: # Procedure Prescription
- set_ip_child_records(inpatient_record, 'procedure_prescription', encounter.procedure_prescription)
-
- if encounter and encounter.therapies: # Therapies
- inpatient_record.therapy_plan = encounter.therapy_plan
- set_ip_child_records(inpatient_record, 'therapies', encounter.therapies)
-
- inpatient_record.status = 'Admission Scheduled'
- inpatient_record.save(ignore_permissions = True)
-
-
-@frappe.whitelist()
-def schedule_discharge(args):
- discharge_order = json.loads(args)
- inpatient_record_id = frappe.db.get_value('Patient', discharge_order['patient'], 'inpatient_record')
- if inpatient_record_id:
- inpatient_record = frappe.get_doc('Inpatient Record', inpatient_record_id)
- check_out_inpatient(inpatient_record)
- set_details_from_ip_order(inpatient_record, discharge_order)
- inpatient_record.status = 'Discharge Scheduled'
- inpatient_record.save(ignore_permissions = True)
- frappe.db.set_value('Patient', discharge_order['patient'], 'inpatient_status', inpatient_record.status)
- frappe.db.set_value('Patient Encounter', inpatient_record.discharge_encounter, 'inpatient_status', inpatient_record.status)
-
-
-def set_details_from_ip_order(inpatient_record, ip_order):
- for key in ip_order:
- inpatient_record.set(key, ip_order[key])
-
-
-def set_ip_child_records(inpatient_record, inpatient_record_child, encounter_child):
- for item in encounter_child:
- table = inpatient_record.append(inpatient_record_child)
- for df in table.meta.get('fields'):
- table.set(df.fieldname, item.get(df.fieldname))
-
-
-def check_out_inpatient(inpatient_record):
- if inpatient_record.inpatient_occupancies:
- for inpatient_occupancy in inpatient_record.inpatient_occupancies:
- if inpatient_occupancy.left != 1:
- inpatient_occupancy.left = True
- inpatient_occupancy.check_out = now_datetime()
- frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant")
-
-
-def discharge_patient(inpatient_record):
- validate_inpatient_invoicing(inpatient_record)
- inpatient_record.discharge_datetime = now_datetime()
- inpatient_record.status = "Discharged"
-
- inpatient_record.save(ignore_permissions = True)
-
-
-def validate_inpatient_invoicing(inpatient_record):
- if frappe.db.get_single_value("Healthcare Settings", "allow_discharge_despite_unbilled_services"):
- return
-
- pending_invoices = get_pending_invoices(inpatient_record)
-
- if pending_invoices:
- message = _("Cannot mark Inpatient Record as Discharged since there are unbilled services. ")
-
- formatted_doc_rows = ''
-
- for doctype, docnames in pending_invoices.items():
- formatted_doc_rows += """
- <td>{0}</td>
- <td>{1}</td>
- </tr>""".format(doctype, docnames)
-
- message += """
- <table class='table'>
- <thead>
- <th>{0}</th>
- <th>{1}</th>
- </thead>
- {2}
- </table>
- """.format(_("Healthcare Service"), _("Documents"), formatted_doc_rows)
-
- frappe.throw(message, title=_("Unbilled Services"), is_minimizable=True, wide=True)
-
-
-def get_pending_invoices(inpatient_record):
- pending_invoices = {}
- if inpatient_record.inpatient_occupancies:
- service_unit_names = False
- for inpatient_occupancy in inpatient_record.inpatient_occupancies:
- if not inpatient_occupancy.invoiced:
- if service_unit_names:
- service_unit_names += ", " + inpatient_occupancy.service_unit
- else:
- service_unit_names = inpatient_occupancy.service_unit
- if service_unit_names:
- pending_invoices["Inpatient Occupancy"] = service_unit_names
-
- docs = ["Patient Appointment", "Patient Encounter", "Lab Test", "Clinical Procedure"]
-
- for doc in docs:
- doc_name_list = get_unbilled_inpatient_docs(doc, inpatient_record)
- if doc_name_list:
- pending_invoices = get_pending_doc(doc, doc_name_list, pending_invoices)
-
- return pending_invoices
-
-
-def get_pending_doc(doc, doc_name_list, pending_invoices):
- if doc_name_list:
- doc_ids = False
- for doc_name in doc_name_list:
- doc_link = get_link_to_form(doc, doc_name.name)
- if doc_ids:
- doc_ids += ", " + doc_link
- else:
- doc_ids = doc_link
- if doc_ids:
- pending_invoices[doc] = doc_ids
-
- return pending_invoices
-
-
-def get_unbilled_inpatient_docs(doc, inpatient_record):
- return frappe.db.get_list(doc, filters = {'patient': inpatient_record.patient,
- 'inpatient_record': inpatient_record.name, 'docstatus': 1, 'invoiced': 0})
-
-
-def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None):
- inpatient_record.admitted_datetime = check_in
- inpatient_record.status = 'Admitted'
- inpatient_record.expected_discharge = expected_discharge
-
- inpatient_record.set('inpatient_occupancies', [])
- transfer_patient(inpatient_record, service_unit, check_in)
-
- frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_status', 'Admitted')
- frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_record', inpatient_record.name)
-
-
-def transfer_patient(inpatient_record, service_unit, check_in):
- item_line = inpatient_record.append('inpatient_occupancies', {})
- item_line.service_unit = service_unit
- item_line.check_in = check_in
-
- inpatient_record.save(ignore_permissions = True)
-
- frappe.db.set_value("Healthcare Service Unit", service_unit, "occupancy_status", "Occupied")
-
-
-def patient_leave_service_unit(inpatient_record, check_out, leave_from):
- if inpatient_record.inpatient_occupancies:
- for inpatient_occupancy in inpatient_record.inpatient_occupancies:
- if inpatient_occupancy.left != 1 and inpatient_occupancy.service_unit == leave_from:
- inpatient_occupancy.left = True
- inpatient_occupancy.check_out = check_out
- frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant")
- inpatient_record.save(ignore_permissions = True)
-
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_leave_from(doctype, txt, searchfield, start, page_len, filters):
- docname = filters['docname']
-
- query = '''select io.service_unit
- from `tabInpatient Occupancy` io, `tabInpatient Record` ir
- where io.parent = '{docname}' and io.parentfield = 'inpatient_occupancies'
- and io.left!=1 and io.parent = ir.name'''
-
- return frappe.db.sql(query.format(**{
- "docname": docname,
- "searchfield": searchfield,
- "mcond": get_match_cond(doctype)
- }), {
- 'txt': "%%%s%%" % txt,
- '_txt': txt.replace("%", ""),
- 'start': start,
- 'page_len': page_len
- })
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record_dashboard.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record_dashboard.py
deleted file mode 100644
index 92cc610..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record_dashboard.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'inpatient_record',
- 'transactions': [
- {
- 'label': _('Appointments and Encounters'),
- 'items': ['Patient Appointment', 'Patient Encounter']
- },
- {
- 'label': _('Lab Tests and Vital Signs'),
- 'items': ['Lab Test', 'Clinical Procedure', 'Sample Collection', 'Vital Signs']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.js
deleted file mode 100644
index 1ce9afa..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Inpatient Record", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Inpatient Record
- () => frappe.tests.make('Inpatient Record', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
deleted file mode 100644
index a8c7720..0000000
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-from frappe.utils import now_datetime, today
-from frappe.utils.make_random import get_random
-from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
-from erpnext.healthcare.doctype.lab_test.test_lab_test import create_patient_encounter
-from erpnext.healthcare.utils import get_encounters_to_invoice
-
-class TestInpatientRecord(unittest.TestCase):
- def test_admit_and_discharge(self):
- frappe.db.sql("""delete from `tabInpatient Record`""")
- patient = create_patient()
- # Schedule Admission
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save(ignore_permissions = True)
- self.assertEqual(ip_record.name, frappe.db.get_value("Patient", patient, "inpatient_record"))
- self.assertEqual(ip_record.status, frappe.db.get_value("Patient", patient, "inpatient_status"))
-
- # Admit
- service_unit = get_healthcare_service_unit()
- admit_patient(ip_record, service_unit, now_datetime())
- self.assertEqual("Admitted", frappe.db.get_value("Patient", patient, "inpatient_status"))
- self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
-
- # Discharge
- schedule_discharge(frappe.as_json({'patient': patient}))
- self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
-
- ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
- # Validate Pending Invoices
- self.assertRaises(frappe.ValidationError, ip_record.discharge)
- mark_invoiced_inpatient_occupancy(ip_record1)
-
- discharge_patient(ip_record1)
-
- self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record"))
- self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status"))
-
- def test_allow_discharge_despite_unbilled_services(self):
- frappe.db.sql("""delete from `tabInpatient Record`""")
- setup_inpatient_settings(key="allow_discharge_despite_unbilled_services", value=1)
- patient = create_patient()
- # Schedule Admission
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save(ignore_permissions = True)
-
- # Admit
- service_unit = get_healthcare_service_unit()
- admit_patient(ip_record, service_unit, now_datetime())
-
- # Discharge
- schedule_discharge(frappe.as_json({"patient": patient}))
- self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
-
- ip_record = frappe.get_doc("Inpatient Record", ip_record.name)
- # Should not validate Pending Invoices
- ip_record.discharge()
-
- self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record"))
- self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status"))
-
- setup_inpatient_settings(key="allow_discharge_despite_unbilled_services", value=0)
-
- def test_do_not_bill_patient_encounters_for_inpatients(self):
- frappe.db.sql("""delete from `tabInpatient Record`""")
- setup_inpatient_settings(key="do_not_bill_inpatient_encounters", value=1)
- patient = create_patient()
- # Schedule Admission
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save(ignore_permissions = True)
-
- # Admit
- service_unit = get_healthcare_service_unit()
- admit_patient(ip_record, service_unit, now_datetime())
-
- # Patient Encounter
- patient_encounter = create_patient_encounter()
- encounters = get_encounters_to_invoice(patient, "_Test Company")
- encounter_ids = [entry.reference_name for entry in encounters]
- self.assertFalse(patient_encounter.name in encounter_ids)
-
- # Discharge
- schedule_discharge(frappe.as_json({"patient": patient}))
- self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
-
- ip_record = frappe.get_doc("Inpatient Record", ip_record.name)
- mark_invoiced_inpatient_occupancy(ip_record)
- discharge_patient(ip_record)
- setup_inpatient_settings(key="do_not_bill_inpatient_encounters", value=0)
-
- def test_validate_overlap_admission(self):
- frappe.db.sql("""delete from `tabInpatient Record`""")
- patient = create_patient()
-
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save(ignore_permissions = True)
- ip_record_new = create_inpatient(patient)
- ip_record_new.expected_length_of_stay = 0
- self.assertRaises(frappe.ValidationError, ip_record_new.save)
-
- service_unit = get_healthcare_service_unit()
- admit_patient(ip_record, service_unit, now_datetime())
- ip_record_new = create_inpatient(patient)
- self.assertRaises(frappe.ValidationError, ip_record_new.save)
- frappe.db.sql("""delete from `tabInpatient Record`""")
-
-def mark_invoiced_inpatient_occupancy(ip_record):
- if ip_record.inpatient_occupancies:
- for inpatient_occupancy in ip_record.inpatient_occupancies:
- inpatient_occupancy.invoiced = 1
- ip_record.save(ignore_permissions = True)
-
-
-def setup_inpatient_settings(key, value):
- settings = frappe.get_single("Healthcare Settings")
- settings.set(key, value)
- settings.save()
-
-
-def create_inpatient(patient):
- patient_obj = frappe.get_doc('Patient', patient)
- inpatient_record = frappe.new_doc('Inpatient Record')
- inpatient_record.patient = patient
- inpatient_record.patient_name = patient_obj.patient_name
- inpatient_record.gender = patient_obj.sex
- inpatient_record.blood_group = patient_obj.blood_group
- inpatient_record.dob = patient_obj.dob
- inpatient_record.mobile = patient_obj.mobile
- inpatient_record.email = patient_obj.email
- inpatient_record.phone = patient_obj.phone
- inpatient_record.inpatient = "Scheduled"
- inpatient_record.scheduled_date = today()
- inpatient_record.company = "_Test Company"
- return inpatient_record
-
-
-def get_healthcare_service_unit(unit_name=None):
- if not unit_name:
- service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1, "company": "_Test Company"})
- else:
- service_unit = frappe.db.exists("Healthcare Service Unit", {"healthcare_service_unit_name": unit_name})
-
- if not service_unit:
- service_unit = frappe.new_doc("Healthcare Service Unit")
- service_unit.healthcare_service_unit_name = unit_name or "Test Service Unit Ip Occupancy"
- service_unit.company = "_Test Company"
- service_unit.service_unit_type = get_service_unit_type()
- service_unit.inpatient_occupancy = 1
- service_unit.occupancy_status = "Vacant"
- service_unit.is_group = 0
- service_unit_parent_name = frappe.db.exists({
- "doctype": "Healthcare Service Unit",
- "healthcare_service_unit_name": "All Healthcare Service Units",
- "is_group": 1
- })
- if not service_unit_parent_name:
- parent_service_unit = frappe.new_doc("Healthcare Service Unit")
- parent_service_unit.healthcare_service_unit_name = "All Healthcare Service Units"
- parent_service_unit.is_group = 1
- parent_service_unit.save(ignore_permissions = True)
- service_unit.parent_healthcare_service_unit = parent_service_unit.name
- else:
- service_unit.parent_healthcare_service_unit = service_unit_parent_name[0][0]
- service_unit.save(ignore_permissions = True)
- return service_unit.name
- return service_unit
-
-
-def get_service_unit_type():
- service_unit_type = get_random("Healthcare Service Unit Type", filters={"inpatient_occupancy": 1})
-
- if not service_unit_type:
- service_unit_type = frappe.new_doc("Healthcare Service Unit Type")
- service_unit_type.service_unit_type = "Test Service Unit Type Ip Occupancy"
- service_unit_type.inpatient_occupancy = 1
- service_unit_type.save(ignore_permissions = True)
- return service_unit_type.name
- return service_unit_type
-
-
-def create_patient():
- patient = frappe.db.exists('Patient', '_Test IPD Patient')
- if not patient:
- patient = frappe.new_doc('Patient')
- patient.first_name = '_Test IPD Patient'
- patient.sex = 'Female'
- patient.save(ignore_permissions=True)
- patient = patient.name
- return patient
diff --git a/erpnext/healthcare/doctype/lab_prescription/__init__.py b/erpnext/healthcare/doctype/lab_prescription/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/lab_prescription/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json
deleted file mode 100644
index 0720bb4..0000000
--- a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.json
+++ /dev/null
@@ -1,78 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-09-16 16:53:06.882970",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "lab_test_code",
- "lab_test_name",
- "invoiced",
- "column_break_4",
- "lab_test_comment",
- "lab_test_created"
- ],
- "fields": [
- {
- "fieldname": "lab_test_code",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Lab Test",
- "options": "Lab Test Template",
- "reqd": 1
- },
- {
- "fetch_from": "lab_test_code.lab_test_name",
- "fieldname": "lab_test_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Lab Test Name"
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "no_copy": 1,
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "lab_test_comment",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Comments"
- },
- {
- "default": "0",
- "fieldname": "lab_test_created",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Test Created",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1,
- "search_index": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-02-26 17:03:00.255560",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Prescription",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.py b/erpnext/healthcare/doctype/lab_prescription/lab_prescription.py
deleted file mode 100644
index b788a0d..0000000
--- a/erpnext/healthcare/doctype/lab_prescription/lab_prescription.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class LabPrescription(Document):
- pass
diff --git a/erpnext/healthcare/doctype/lab_test/__init__.py b/erpnext/healthcare/doctype/lab_test/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/lab_test/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js
deleted file mode 100644
index bb7976c..0000000
--- a/erpnext/healthcare/doctype/lab_test/lab_test.js
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright (c) 2016, ESS and contributors
-// For license information, please see license.txt
-
-cur_frm.cscript.custom_refresh = function (doc) {
- cur_frm.toggle_display('sb_sensitivity', doc.sensitivity_toggle);
- cur_frm.toggle_display('organisms_section', doc.descriptive_toggle);
- cur_frm.toggle_display('sb_descriptive', doc.descriptive_toggle);
- cur_frm.toggle_display('sb_normal', doc.normal_toggle);
-};
-
-frappe.ui.form.on('Lab Test', {
- setup: function (frm) {
- frm.get_field('normal_test_items').grid.editable_fields = [
- { fieldname: 'lab_test_name', columns: 3 },
- { fieldname: 'lab_test_event', columns: 2 },
- { fieldname: 'result_value', columns: 2 },
- { fieldname: 'lab_test_uom', columns: 1 },
- { fieldname: 'normal_range', columns: 2 }
- ];
- frm.get_field('descriptive_test_items').grid.editable_fields = [
- { fieldname: 'lab_test_particulars', columns: 3 },
- { fieldname: 'result_value', columns: 7 }
- ];
- },
- refresh: function (frm) {
- refresh_field('normal_test_items');
- refresh_field('descriptive_test_items');
- if (frm.doc.__islocal) {
- frm.add_custom_button(__('Get from Patient Encounter'), function () {
- get_lab_test_prescribed(frm);
- });
- }
- if (frappe.defaults.get_default('lab_test_approval_required') && frappe.user.has_role('LabTest Approver')) {
- if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') {
- frm.add_custom_button(__('Approve'), function () {
- status_update(1, frm);
- }, __('Actions'));
- frm.add_custom_button(__('Reject'), function () {
- status_update(0, frm);
- }, __('Actions'));
- }
- }
-
- if (frm.doc.docstatus === 1 && frm.doc.sms_sent === 0 && frm.doc.status !== 'Rejected' ) {
- frm.add_custom_button(__('Send SMS'), function () {
- frappe.call({
- method: 'erpnext.healthcare.doctype.healthcare_settings.healthcare_settings.get_sms_text',
- args: { doc: frm.doc.name },
- callback: function (r) {
- if (!r.exc) {
- var emailed = r.message.emailed;
- var printed = r.message.printed;
- make_dialog(frm, emailed, printed);
- }
- }
- });
- });
- }
-
- }
-});
-
-frappe.ui.form.on('Lab Test', 'patient', function (frm) {
- if (frm.doc.patient) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
- args: { patient: frm.doc.patient },
- callback: function (data) {
- var age = null;
- if (data.message.dob) {
- age = calculate_age(data.message.dob);
- }
- let values = {
- 'patient_age': age,
- 'patient_sex': data.message.sex,
- 'email': data.message.email,
- 'mobile': data.message.mobile,
- 'report_preference': data.message.report_preference
- };
- frm.set_value(values);
- }
- });
- }
-});
-
-frappe.ui.form.on('Normal Test Result', {
- normal_test_items_remove: function () {
- frappe.msgprint(__('Not permitted, configure Lab Test Template as required'));
- cur_frm.reload_doc();
- }
-});
-
-frappe.ui.form.on('Descriptive Test Result', {
- descriptive_test_items_remove: function () {
- frappe.msgprint(__('Not permitted, configure Lab Test Template as required'));
- cur_frm.reload_doc();
- }
-});
-
-var status_update = function (approve, frm) {
- var doc = frm.doc;
- var status = null;
- if (approve == 1) {
- status = 'Approved';
- }
- else {
- status = 'Rejected';
- }
- frappe.call({
- method: 'erpnext.healthcare.doctype.lab_test.lab_test.update_status',
- args: { status: status, name: doc.name },
- callback: function () {
- cur_frm.reload_doc();
- }
- });
-};
-
-var get_lab_test_prescribed = function (frm) {
- if (frm.doc.patient) {
- frappe.call({
- method: 'erpnext.healthcare.doctype.lab_test.lab_test.get_lab_test_prescribed',
- args: { patient: frm.doc.patient },
- callback: function (r) {
- show_lab_tests(frm, r.message);
- }
- });
- }
- else {
- frappe.msgprint(__('Please select Patient to get Lab Tests'));
- }
-};
-
-var show_lab_tests = function (frm, lab_test_list) {
- var d = new frappe.ui.Dialog({
- title: __('Lab Tests'),
- fields: [{
- fieldtype: 'HTML', fieldname: 'lab_test'
- }]
- });
- var html_field = d.fields_dict.lab_test.$wrapper;
- html_field.empty();
- $.each(lab_test_list, function (x, y) {
- var row = $(repl(
- '<div class="col-xs-12" style="padding-top:12px;">\
- <div class="col-xs-3"> %(lab_test)s </div>\
- <div class="col-xs-4"> %(practitioner_name)s<br>%(encounter)s</div>\
- <div class="col-xs-3"> %(date)s </div>\
- <div class="col-xs-1">\
- <a data-name="%(name)s" data-lab-test="%(lab_test)s"\
- data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
- data-invoiced="%(invoiced)s" href="#"><button class="btn btn-default btn-xs">Get</button></a>\
- </div>\
- </div><hr>',
- { name: y[0], lab_test: y[1], encounter: y[2], invoiced: y[3], practitioner: y[4], practitioner_name: y[5], date: y[6] })
- ).appendTo(html_field);
-
- row.find("a").click(function () {
- frm.doc.template = $(this).attr('data-lab-test');
- frm.doc.prescription = $(this).attr('data-name');
- frm.doc.practitioner = $(this).attr('data-practitioner');
- frm.set_df_property('template', 'read_only', 1);
- frm.set_df_property('patient', 'read_only', 1);
- frm.set_df_property('practitioner', 'read_only', 1);
- frm.doc.invoiced = 0;
- if ($(this).attr('data-invoiced') === 1) {
- frm.doc.invoiced = 1;
- }
- refresh_field('invoiced');
- refresh_field('template');
- d.hide();
- return false;
- });
- });
- if (!lab_test_list.length) {
- var msg = __('No Lab Tests found for the Patient {0}', [frm.doc.patient_name.bold()]);
- html_field.empty();
- $(repl('<div class="col-xs-12" style="padding-top:0px;" >%(msg)s</div>', { msg: msg })).appendTo(html_field);
- }
- d.show();
-};
-
-var make_dialog = function (frm, emailed, printed) {
- var number = frm.doc.mobile;
-
- var dialog = new frappe.ui.Dialog({
- title: 'Send SMS',
- width: 400,
- fields: [
- { fieldname: 'result_format', fieldtype: 'Select', label: 'Result Format', options: ['Emailed', 'Printed'] },
- { fieldname: 'number', fieldtype: 'Data', label: 'Mobile Number', reqd: 1 },
- { fieldname: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 }
- ],
- primary_action_label: __('Send'),
- primary_action: function () {
- var values = dialog.fields_dict;
- if (!values) {
- return;
- }
- send_sms(values, frm);
- dialog.hide();
- }
- });
- if (frm.doc.report_preference === 'Print') {
- dialog.set_values({
- 'result_format': 'Printed',
- 'number': number,
- 'message': printed
- });
- } else {
- dialog.set_values({
- 'result_format': 'Emailed',
- 'number': number,
- 'message': emailed
- });
- }
- var fd = dialog.fields_dict;
- $(fd.result_format.input).change(function () {
- if (dialog.get_value('result_format') === 'Emailed') {
- dialog.set_values({
- 'number': number,
- 'message': emailed
- });
- } else {
- dialog.set_values({
- 'number': number,
- 'message': printed
- });
- }
- });
- dialog.show();
-};
-
-var send_sms = function (vals, frm) {
- var number = vals.number.value;
- var message = vals.message.last_value;
-
- if (!number || !message) {
- frappe.throw(__('Did not send SMS, missing patient mobile number or message content.'));
- }
- frappe.call({
- method: 'frappe.core.doctype.sms_settings.sms_settings.send_sms',
- args: {
- receiver_list: [number],
- msg: message
- },
- callback: function (r) {
- if (r.exc) {
- frappe.msgprint(r.exc);
- } else {
- frm.reload_doc();
- }
- }
- });
-};
-
-var calculate_age = function (dob) {
- var ageMS = Date.parse(Date()) - Date.parse(dob);
- var age = new Date();
- age.setTime(ageMS);
- var years = age.getFullYear() - 1970;
- return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
-};
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json
deleted file mode 100644
index ac61fea..0000000
--- a/erpnext/healthcare/doctype/lab_test/lab_test.json
+++ /dev/null
@@ -1,610 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2016-03-29 17:34:47.509094",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "template",
- "lab_test_name",
- "lab_test_group",
- "medical_code",
- "department",
- "column_break_26",
- "company",
- "status",
- "submitted_date",
- "result_date",
- "approved_date",
- "expected_result_date",
- "expected_result_time",
- "printed_on",
- "invoiced",
- "sb_first",
- "patient",
- "patient_name",
- "patient_age",
- "patient_sex",
- "inpatient_record",
- "report_preference",
- "email",
- "mobile",
- "c_b",
- "practitioner",
- "practitioner_name",
- "requesting_department",
- "employee",
- "employee_name",
- "employee_designation",
- "user",
- "sample",
- "sb_normal",
- "lab_test_html",
- "normal_test_items",
- "sb_descriptive",
- "descriptive_test_items",
- "organisms_section",
- "organism_test_items",
- "sb_sensitivity",
- "sensitivity_test_items",
- "sb_comments",
- "lab_test_comment",
- "sb_customresult",
- "custom_result",
- "worksheet_section",
- "worksheet_instructions",
- "result_legend_section",
- "legend_print_position",
- "result_legend",
- "section_break_50",
- "email_sent",
- "sms_sent",
- "printed",
- "normal_toggle",
- "descriptive_toggle",
- "sensitivity_toggle",
- "amended_from",
- "prescription"
- ],
- "fields": [
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-LAB-.YYYY.-",
- "print_hide": 1,
- "report_hide": 1,
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "no_copy": 1,
- "read_only": 1,
- "search_index": 1
- },
- {
- "fetch_from": "inpatient_record.patient",
- "fieldname": "patient",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1,
- "search_index": 1,
- "set_only_once": 1
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "label": "Age",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "patient_sex",
- "fieldtype": "Link",
- "label": "Gender",
- "options": "Gender",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1,
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Requesting Practitioner",
- "no_copy": 1,
- "options": "Healthcare Practitioner",
- "search_index": 1
- },
- {
- "fetch_from": "patient.email",
- "fieldname": "email",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Email",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "patient.mobile",
- "fieldname": "mobile",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Mobile",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1,
- "search_index": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "c_b",
- "fieldtype": "Column Break",
- "print_hide": 1
- },
- {
- "fetch_from": "template.department",
- "fieldname": "department",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Department",
- "options": "Medical Department",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Status",
- "options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "submitted_date",
- "fieldtype": "Datetime",
- "hidden": 1,
- "label": "Submitted Date",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "approved_date",
- "fieldtype": "Datetime",
- "hidden": 1,
- "label": "Approved Date",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "sample",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_filter": 1,
- "label": "Sample ID",
- "options": "Sample Collection",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "default": "Today",
- "fieldname": "expected_result_date",
- "fieldtype": "Date",
- "hidden": 1,
- "label": "Expected Result Date",
- "read_only": 1
- },
- {
- "fieldname": "expected_result_time",
- "fieldtype": "Time",
- "hidden": 1,
- "label": "Expected Result Time",
- "read_only": 1
- },
- {
- "fieldname": "result_date",
- "fieldtype": "Date",
- "label": "Result Date",
- "read_only": 1,
- "search_index": 1
- },
- {
- "allow_on_submit": 1,
- "fieldname": "printed_on",
- "fieldtype": "Datetime",
- "label": "Printed on",
- "read_only": 1
- },
- {
- "fieldname": "employee",
- "fieldtype": "Link",
- "label": "Employee (Lab Technician)",
- "no_copy": 1,
- "options": "Employee",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "employee.employee_name",
- "fieldname": "employee_name",
- "fieldtype": "Data",
- "label": "Lab Technician Name",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "employee.designation",
- "fieldname": "employee_designation",
- "fieldtype": "Data",
- "label": "Lab Technician Designation",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "User",
- "no_copy": 1,
- "options": "User",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "patient.report_preference",
- "fieldname": "report_preference",
- "fieldtype": "Data",
- "label": "Report Preference",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "sb_first",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "lab_test_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Test Name",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1,
- "search_index": 1
- },
- {
- "fieldname": "template",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Test Template",
- "options": "Lab Test Template",
- "print_hide": 1,
- "report_hide": 1,
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "lab_test_group",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Test Group",
- "options": "Item Group",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "template.medical_code",
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "label": "Medical Code",
- "options": "Medical Code",
- "read_only": 1
- },
- {
- "fieldname": "sb_normal",
- "fieldtype": "Section Break",
- "label": "Compound Test Result"
- },
- {
- "fieldname": "normal_test_items",
- "fieldtype": "Table",
- "label": "Normal Test Result",
- "options": "Normal Test Result",
- "print_hide": 1
- },
- {
- "fieldname": "lab_test_html",
- "fieldtype": "HTML"
- },
- {
- "depends_on": "descriptive_toggle",
- "fieldname": "organisms_section",
- "fieldtype": "Section Break",
- "label": "Organism Test Result"
- },
- {
- "fieldname": "sb_sensitivity",
- "fieldtype": "Section Break",
- "label": "Sensitivity Test Result"
- },
- {
- "fieldname": "sensitivity_test_items",
- "fieldtype": "Table",
- "label": "Sensitivity Test Result",
- "options": "Sensitivity Test Result",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "sb_comments",
- "fieldtype": "Section Break",
- "label": "Comments"
- },
- {
- "fieldname": "lab_test_comment",
- "fieldtype": "Text",
- "ignore_xss_filter": 1,
- "label": "Comments",
- "print_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "sb_customresult",
- "fieldtype": "Section Break",
- "label": "Custom Result"
- },
- {
- "fieldname": "custom_result",
- "fieldtype": "Text Editor",
- "ignore_xss_filter": 1,
- "label": "Custom Result",
- "print_hide": 1
- },
- {
- "default": "0",
- "fieldname": "email_sent",
- "fieldtype": "Check",
- "hidden": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "sms_sent",
- "fieldtype": "Check",
- "hidden": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "printed",
- "fieldtype": "Check",
- "hidden": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "normal_toggle",
- "fieldtype": "Check",
- "hidden": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "sensitivity_toggle",
- "fieldtype": "Check",
- "hidden": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Lab Test",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "prescription",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Prescription",
- "no_copy": 1,
- "options": "Lab Prescription",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "column_break_26",
- "fieldtype": "Column Break"
- },
- {
- "fetch_from": "practitioner.department",
- "fieldname": "requesting_department",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Requesting Department",
- "options": "Medical Department",
- "read_only": 1
- },
- {
- "fetch_from": "practitioner.practitioner_name",
- "fieldname": "practitioner_name",
- "fieldtype": "Data",
- "label": "Requesting Practitioner",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "result_legend_section",
- "fieldtype": "Section Break",
- "label": "Result Legend Print"
- },
- {
- "fieldname": "legend_print_position",
- "fieldtype": "Select",
- "label": "Print Position",
- "options": "\nBottom\nTop\nBoth",
- "print_hide": 1
- },
- {
- "fieldname": "result_legend",
- "fieldtype": "Text Editor",
- "label": "Result Legend",
- "print_hide": 1
- },
- {
- "fieldname": "section_break_50",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "worksheet_instructions",
- "fieldtype": "Text Editor",
- "label": "Worksheet Instructions",
- "print_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "worksheet_section",
- "fieldtype": "Section Break",
- "label": "Worksheet Print"
- },
- {
- "fieldname": "descriptive_test_items",
- "fieldtype": "Table",
- "label": "Descriptive Test Result",
- "options": "Descriptive Test Result",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "sb_descriptive",
- "fieldtype": "Section Break",
- "label": "Descriptive Test Result"
- },
- {
- "default": "0",
- "fieldname": "descriptive_toggle",
- "fieldtype": "Check",
- "hidden": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "organism_test_items",
- "fieldtype": "Table",
- "label": "Organism Test Result",
- "options": "Organism Test Result",
- "print_hide": 1
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-11-30 11:04:17.195848",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "LabTest Approver",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient,practitioner,lab_test_name,sample",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py
deleted file mode 100644
index 4b57cd0..0000000
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ /dev/null
@@ -1,350 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import getdate, cstr, get_link_to_form
-
-class LabTest(Document):
- def validate(self):
- if not self.is_new():
- self.set_secondary_uom_result()
-
- def on_submit(self):
- self.validate_result_values()
- self.db_set('submitted_date', getdate())
- self.db_set('status', 'Completed')
-
- def on_cancel(self):
- self.db_set('status', 'Cancelled')
- self.reload()
-
- def on_update(self):
- if self.sensitivity_test_items:
- sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity)
- for i, item in enumerate(sensitivity):
- item.idx = i + 1
- self.sensitivity_test_items = sensitivity
-
- def after_insert(self):
- if self.prescription:
- frappe.db.set_value('Lab Prescription', self.prescription, 'lab_test_created', 1)
- if frappe.db.get_value('Lab Prescription', self.prescription, 'invoiced'):
- self.invoiced = True
- if not self.lab_test_name and self.template:
- self.load_test_from_template()
- self.reload()
-
- def load_test_from_template(self):
- lab_test = self
- create_test_from_template(lab_test)
- self.reload()
-
- def set_secondary_uom_result(self):
- for item in self.normal_test_items:
- if item.result_value and item.secondary_uom and item.conversion_factor:
- try:
- item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
- except:
- item.secondary_uom_result = ''
- frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning'))
-
- def validate_result_values(self):
- if self.normal_test_items:
- for item in self.normal_test_items:
- if not item.result_value and not item.allow_blank and item.require_result_value:
- frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
- item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results'))
-
- if self.descriptive_test_items:
- for item in self.descriptive_test_items:
- if not item.result_value and not item.allow_blank and item.require_result_value:
- frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
- item.idx, frappe.bold(item.lab_test_particulars)), title=_('Mandatory Results'))
-
-
-def create_test_from_template(lab_test):
- template = frappe.get_doc('Lab Test Template', lab_test.template)
- patient = frappe.get_doc('Patient', lab_test.patient)
-
- lab_test.lab_test_name = template.lab_test_name
- lab_test.result_date = getdate()
- lab_test.department = template.department
- lab_test.lab_test_group = template.lab_test_group
- lab_test.legend_print_position = template.legend_print_position
- lab_test.result_legend = template.result_legend
- lab_test.worksheet_instructions = template.worksheet_instructions
-
- lab_test = create_sample_collection(lab_test, template, patient, None)
- lab_test = load_result_format(lab_test, template, None, None)
-
-@frappe.whitelist()
-def update_status(status, name):
- if name and status:
- frappe.db.set_value('Lab Test', name, {
- 'status': status,
- 'approved_date': getdate()
- })
-
-@frappe.whitelist()
-def create_multiple(doctype, docname):
- if not doctype or not docname:
- frappe.throw(_('Sales Invoice or Patient Encounter is required to create Lab Tests'), title=_('Insufficient Data'))
-
- lab_test_created = False
- if doctype == 'Sales Invoice':
- lab_test_created = create_lab_test_from_invoice(docname)
- elif doctype == 'Patient Encounter':
- lab_test_created = create_lab_test_from_encounter(docname)
-
- if lab_test_created:
- frappe.msgprint(_('Lab Test(s) {0} created successfully').format(lab_test_created), indicator='green')
- else:
- frappe.msgprint(_('No Lab Tests created'))
-
-def create_lab_test_from_encounter(encounter):
- lab_test_created = False
- encounter = frappe.get_doc('Patient Encounter', encounter)
-
- if encounter and encounter.lab_test_prescription:
- patient = frappe.get_doc('Patient', encounter.patient)
- for item in encounter.lab_test_prescription:
- if not item.lab_test_created:
- template = get_lab_test_template(item.lab_test_code)
- if template:
- lab_test = create_lab_test_doc(item.invoiced, encounter.practitioner, patient, template, encounter.company)
- lab_test.save(ignore_permissions = True)
- frappe.db.set_value('Lab Prescription', item.name, 'lab_test_created', 1)
- if not lab_test_created:
- lab_test_created = lab_test.name
- else:
- lab_test_created += ', ' + lab_test.name
- return lab_test_created
-
-
-def create_lab_test_from_invoice(sales_invoice):
- lab_tests_created = False
- invoice = frappe.get_doc('Sales Invoice', sales_invoice)
- if invoice and invoice.patient:
- patient = frappe.get_doc('Patient', invoice.patient)
- for item in invoice.items:
- lab_test_created = 0
- if item.reference_dt == 'Lab Prescription':
- lab_test_created = frappe.db.get_value('Lab Prescription', item.reference_dn, 'lab_test_created')
- elif item.reference_dt == 'Lab Test':
- lab_test_created = 1
- if lab_test_created != 1:
- template = get_lab_test_template(item.item_code)
- if template:
- lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, invoice.company)
- if item.reference_dt == 'Lab Prescription':
- lab_test.prescription = item.reference_dn
- lab_test.save(ignore_permissions = True)
- if item.reference_dt != 'Lab Prescription':
- frappe.db.set_value('Sales Invoice Item', item.name, 'reference_dt', 'Lab Test')
- frappe.db.set_value('Sales Invoice Item', item.name, 'reference_dn', lab_test.name)
- if not lab_tests_created:
- lab_tests_created = lab_test.name
- else:
- lab_tests_created += ', ' + lab_test.name
- return lab_tests_created
-
-def get_lab_test_template(item):
- template_id = frappe.db.exists('Lab Test Template', {'item': item})
- if template_id:
- return frappe.get_doc('Lab Test Template', template_id)
- return False
-
-def create_lab_test_doc(invoiced, practitioner, patient, template, company):
- lab_test = frappe.new_doc('Lab Test')
- lab_test.invoiced = invoiced
- lab_test.practitioner = practitioner
- lab_test.patient = patient.name
- lab_test.patient_age = patient.get_age()
- lab_test.patient_sex = patient.sex
- lab_test.email = patient.email
- lab_test.mobile = patient.mobile
- lab_test.report_preference = patient.report_preference
- lab_test.department = template.department
- lab_test.template = template.name
- lab_test.lab_test_group = template.lab_test_group
- lab_test.result_date = getdate()
- lab_test.company = company
- return lab_test
-
-def create_normals(template, lab_test):
- lab_test.normal_toggle = 1
- normal = lab_test.append('normal_test_items')
- normal.lab_test_name = template.lab_test_name
- normal.lab_test_uom = template.lab_test_uom
- normal.secondary_uom = template.secondary_uom
- normal.conversion_factor = template.conversion_factor
- normal.normal_range = template.lab_test_normal_range
- normal.require_result_value = 1
- normal.allow_blank = 0
- normal.template = template.name
-
-def create_compounds(template, lab_test, is_group):
- lab_test.normal_toggle = 1
- for normal_test_template in template.normal_test_templates:
- normal = lab_test.append('normal_test_items')
- if is_group:
- normal.lab_test_event = normal_test_template.lab_test_event
- else:
- normal.lab_test_name = normal_test_template.lab_test_event
-
- normal.lab_test_uom = normal_test_template.lab_test_uom
- normal.secondary_uom = normal_test_template.secondary_uom
- normal.conversion_factor = normal_test_template.conversion_factor
- normal.normal_range = normal_test_template.normal_range
- normal.require_result_value = 1
- normal.allow_blank = normal_test_template.allow_blank
- normal.template = template.name
-
-def create_descriptives(template, lab_test):
- lab_test.descriptive_toggle = 1
- if template.sensitivity:
- lab_test.sensitivity_toggle = 1
- for descriptive_test_template in template.descriptive_test_templates:
- descriptive = lab_test.append('descriptive_test_items')
- descriptive.lab_test_particulars = descriptive_test_template.particulars
- descriptive.require_result_value = 1
- descriptive.allow_blank = descriptive_test_template.allow_blank
- descriptive.template = template.name
-
-def create_sample_doc(template, patient, invoice, company = None):
- if template.sample:
- sample_exists = frappe.db.exists({
- 'doctype': 'Sample Collection',
- 'patient': patient.name,
- 'docstatus': 0,
- 'sample': template.sample
- })
-
- if sample_exists:
- # update sample collection by adding quantity
- sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0])
- quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
- if template.sample_details:
- sample_details = sample_collection.sample_details + '\n-\n' + _('Test: ')
- sample_details += (template.get('lab_test_name') or template.get('template')) + '\n'
- sample_details += _('Collection Details: ') + '\n\t' + template.sample_details
- frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_details', sample_details)
-
- frappe.db.set_value('Sample Collection', sample_collection.name, 'sample_qty', quantity)
-
- else:
- # Create Sample Collection for template, copy vals from Invoice
- sample_collection = frappe.new_doc('Sample Collection')
- if invoice:
- sample_collection.invoiced = True
-
- sample_collection.patient = patient.name
- sample_collection.patient_age = patient.get_age()
- sample_collection.patient_sex = patient.sex
- sample_collection.sample = template.sample
- sample_collection.sample_uom = template.sample_uom
- sample_collection.sample_qty = template.sample_qty
- sample_collection.company = company
-
- if template.sample_details:
- sample_collection.sample_details = _('Test :') + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
- sample_collection.save(ignore_permissions=True)
-
- return sample_collection
-
-def create_sample_collection(lab_test, template, patient, invoice):
- if frappe.get_cached_value('Healthcare Settings', None, 'create_sample_collection_for_lab_test'):
- sample_collection = create_sample_doc(template, patient, invoice, lab_test.company)
- if sample_collection:
- lab_test.sample = sample_collection.name
- sample_collection_doc = get_link_to_form('Sample Collection', sample_collection.name)
- frappe.msgprint(_('Sample Collection {0} has been created').format(sample_collection_doc),
- title=_('Sample Collection'), indicator='green')
- return lab_test
-
-def load_result_format(lab_test, template, prescription, invoice):
- if template.lab_test_template_type == 'Single':
- create_normals(template, lab_test)
-
- elif template.lab_test_template_type == 'Compound':
- create_compounds(template, lab_test, False)
-
- elif template.lab_test_template_type == 'Descriptive':
- create_descriptives(template, lab_test)
-
- elif template.lab_test_template_type == 'Grouped':
- # Iterate for each template in the group and create one result for all.
- for lab_test_group in template.lab_test_groups:
- # Template_in_group = None
- if lab_test_group.lab_test_template:
- template_in_group = frappe.get_doc('Lab Test Template', lab_test_group.lab_test_template)
- if template_in_group:
- if template_in_group.lab_test_template_type == 'Single':
- create_normals(template_in_group, lab_test)
-
- elif template_in_group.lab_test_template_type == 'Compound':
- normal_heading = lab_test.append('normal_test_items')
- normal_heading.lab_test_name = template_in_group.lab_test_name
- normal_heading.require_result_value = 0
- normal_heading.allow_blank = 1
- normal_heading.template = template_in_group.name
- create_compounds(template_in_group, lab_test, True)
-
- elif template_in_group.lab_test_template_type == 'Descriptive':
- descriptive_heading = lab_test.append('descriptive_test_items')
- descriptive_heading.lab_test_name = template_in_group.lab_test_name
- descriptive_heading.require_result_value = 0
- descriptive_heading.allow_blank = 1
- descriptive_heading.template = template_in_group.name
- create_descriptives(template_in_group, lab_test)
-
- else: # Lab Test Group - Add New Line
- normal = lab_test.append('normal_test_items')
- normal.lab_test_name = lab_test_group.group_event
- normal.lab_test_uom = lab_test_group.group_test_uom
- normal.secondary_uom = lab_test_group.secondary_uom
- normal.conversion_factor = lab_test_group.conversion_factor
- normal.normal_range = lab_test_group.group_test_normal_range
- normal.allow_blank = lab_test_group.allow_blank
- normal.require_result_value = 1
- normal.template = template.name
-
- if template.lab_test_template_type != 'No Result':
- if prescription:
- lab_test.prescription = prescription
- if invoice:
- frappe.db.set_value('Lab Prescription', prescription, 'invoiced', True)
- lab_test.save(ignore_permissions=True) # Insert the result
- return lab_test
-
-@frappe.whitelist()
-def get_employee_by_user_id(user_id):
- emp_id = frappe.db.exists('Employee', { 'user_id': user_id })
- if emp_id:
- return frappe.get_doc('Employee', emp_id)
- return None
-
-
-@frappe.whitelist()
-def get_lab_test_prescribed(patient):
- return frappe.db.sql(
- '''
- select
- lp.name,
- lp.lab_test_code,
- lp.parent,
- lp.invoiced,
- pe.practitioner,
- pe.practitioner_name,
- pe.encounter_date
- from
- `tabPatient Encounter` pe, `tabLab Prescription` lp
- where
- pe.patient=%s
- and lp.parent=pe.name
- and lp.lab_test_created=0
- ''', (patient))
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js
deleted file mode 100644
index 7b5b9d9..0000000
--- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-(c) ESS 2015-16
-*/
-frappe.listview_settings['Lab Test'] = {
- add_fields: ['name', 'status', 'invoiced'],
- filters: [['docstatus', '=', '1']],
- get_indicator: function (doc) {
- if (doc.status === 'Approved') {
- return [__('Approved'), 'green', 'status, =, Approved'];
- } else if (doc.status === 'Rejected') {
- return [__('Rejected'), 'orange', 'status, =, Rejected'];
- } else if (doc.status === 'Completed') {
- return [__('Completed'), 'green', 'status, =, Completed'];
- } else if (doc.status === 'Cancelled') {
- return [__('Cancelled'), 'red', 'status, =, Cancelled'];
- }
- },
- onload: function (listview) {
- listview.page.add_menu_item(__('Create Multiple'), function () {
- create_multiple_dialog(listview);
- });
- }
-};
-
-var create_multiple_dialog = function (listview) {
- var dialog = new frappe.ui.Dialog({
- title: 'Create Multiple Lab Tests',
- width: 100,
- fields: [
- { fieldtype: 'Link', label: 'Patient', fieldname: 'patient', options: 'Patient', reqd: 1 },
- {
- fieldtype: 'Select', label: 'Invoice / Patient Encounter', fieldname: 'doctype',
- options: '\nSales Invoice\nPatient Encounter', reqd: 1
- },
- {
- fieldtype: 'Dynamic Link', fieldname: 'docname', options: 'doctype', reqd: 1,
- get_query: function () {
- return {
- filters: {
- 'patient': dialog.get_value('patient'),
- 'docstatus': 1
- }
- };
- }
- }
- ],
- primary_action_label: __('Create'),
- primary_action: function () {
- frappe.call({
- method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple',
- args: {
- 'doctype': dialog.get_value('doctype'),
- 'docname': dialog.get_value('docname')
- },
- callback: function (data) {
- if (!data.exc) {
- if (!data.message) {
- frappe.msgprint(__('No Lab Tests created'));
- }
- listview.refresh();
- }
- },
- freeze: true,
- freeze_message: __('Creating Lab Tests...')
- });
- dialog.hide();
- }
- });
-
- dialog.show();
-};
diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.js b/erpnext/healthcare/doctype/lab_test/test_lab_test.js
deleted file mode 100644
index 57cb22b..0000000
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Lab Test
- () => frappe.tests.make('Lab Test', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
deleted file mode 100644
index c9f0029..0000000
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py
+++ /dev/null
@@ -1,206 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-import frappe
-from frappe.utils import getdate, nowtime
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
-from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
-from erpnext.healthcare.doctype.patient_medical_record.test_patient_medical_record import create_lab_test_template as create_blood_test_template
-
-class TestLabTest(unittest.TestCase):
- def test_lab_test_item(self):
- lab_template = create_lab_test_template()
- self.assertTrue(frappe.db.exists('Item', lab_template.item))
- self.assertEqual(frappe.db.get_value('Item Price', {'item_code':lab_template.item}, 'price_list_rate'), lab_template.lab_test_rate)
-
- lab_template.disabled = 1
- lab_template.save()
- self.assertEqual(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1)
-
- lab_template.reload()
-
- lab_template.disabled = 0
- lab_template.save()
-
- def test_descriptive_lab_test(self):
- lab_template = create_lab_test_template()
-
- # blank result value not allowed as per template
- lab_test = create_lab_test(lab_template)
- lab_test.descriptive_test_items[0].result_value = 12
- lab_test.descriptive_test_items[2].result_value = 1
- lab_test.save()
- self.assertRaises(frappe.ValidationError, lab_test.submit)
-
- def test_sample_collection(self):
- frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 1)
- lab_template = create_lab_test_template()
-
- lab_test = create_lab_test(lab_template)
- lab_test.descriptive_test_items[0].result_value = 12
- lab_test.descriptive_test_items[1].result_value = 1
- lab_test.descriptive_test_items[2].result_value = 2.3
- lab_test.save()
-
- # check sample collection created
- self.assertTrue(frappe.db.exists('Sample Collection', {'sample': lab_template.sample}))
-
- frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 0)
- lab_test = create_lab_test(lab_template)
- lab_test.descriptive_test_items[0].result_value = 12
- lab_test.descriptive_test_items[1].result_value = 1
- lab_test.descriptive_test_items[2].result_value = 2.3
- lab_test.save()
-
- # sample collection should not be created
- lab_test.reload()
- self.assertEqual(lab_test.sample, None)
-
- def test_create_lab_tests_from_sales_invoice(self):
- sales_invoice = create_sales_invoice()
- create_multiple('Sales Invoice', sales_invoice.name)
- sales_invoice.reload()
- self.assertIsNotNone(sales_invoice.items[0].reference_dn)
- self.assertIsNotNone(sales_invoice.items[1].reference_dn)
-
- def test_create_lab_tests_from_patient_encounter(self):
- patient_encounter = create_patient_encounter()
- create_multiple('Patient Encounter', patient_encounter.name)
- patient_encounter.reload()
- self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
- self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
-
-
-def create_lab_test_template(test_sensitivity=0, sample_collection=1):
- medical_department = create_medical_department()
- if frappe.db.exists('Lab Test Template', 'Insulin Resistance'):
- return frappe.get_doc('Lab Test Template', 'Insulin Resistance')
- template = frappe.new_doc('Lab Test Template')
- template.lab_test_name = 'Insulin Resistance'
- template.lab_test_template_type = 'Descriptive'
- template.lab_test_code = 'Insulin Resistance'
- template.lab_test_group = 'Services'
- template.department = medical_department
- template.is_billable = 1
- template.lab_test_description = 'Insulin Resistance'
- template.lab_test_rate = 2000
-
- for entry in ['FBS', 'Insulin', 'IR']:
- template.append('descriptive_test_templates', {
- 'particulars': entry,
- 'allow_blank': 1 if entry=='IR' else 0
- })
-
- if test_sensitivity:
- template.sensitivity = 1
-
- if sample_collection:
- template.sample = create_lab_test_sample()
- template.sample_qty = 5.0
-
- template.save()
- return template
-
-def create_medical_department():
- medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
- if not medical_department:
- medical_department = frappe.new_doc('Medical Department')
- medical_department.department = '_Test Medical Department'
- medical_department.save()
- medical_department = medical_department.name
-
- return medical_department
-
-def create_lab_test(lab_template):
- patient = create_patient()
- lab_test = frappe.new_doc('Lab Test')
- lab_test.template = lab_template.name
- lab_test.patient = patient
- lab_test.patient_sex = 'Female'
- lab_test.save()
-
- return lab_test
-
-def create_lab_test_sample():
- blood_sample = frappe.db.exists('Lab Test Sample', 'Blood Sample')
- if blood_sample:
- return blood_sample
-
- sample = frappe.new_doc('Lab Test Sample')
- sample.sample = 'Blood Sample'
- sample.sample_uom = 'U/ml'
- sample.save()
-
- return sample.name
-
-def create_sales_invoice():
- patient = create_patient()
- medical_department = create_medical_department()
- insulin_resistance_template = create_lab_test_template()
- blood_test_template = create_blood_test_template(medical_department)
-
- sales_invoice = frappe.new_doc('Sales Invoice')
- sales_invoice.patient = patient
- sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer')
- sales_invoice.due_date = getdate()
- sales_invoice.company = '_Test Company'
- sales_invoice.debit_to = get_receivable_account('_Test Company')
-
- tests = [insulin_resistance_template, blood_test_template]
- for entry in tests:
- sales_invoice.append('items', {
- 'item_code': entry.item,
- 'item_name': entry.lab_test_name,
- 'description': entry.lab_test_description,
- 'qty': 1,
- 'uom': 'Nos',
- 'conversion_factor': 1,
- 'income_account': get_income_account(None, '_Test Company'),
- 'rate': entry.lab_test_rate,
- 'amount': entry.lab_test_rate
- })
-
- sales_invoice.set_missing_values()
-
- sales_invoice.submit()
- return sales_invoice
-
-def create_patient_encounter():
- patient = create_patient()
- medical_department = create_medical_department()
- insulin_resistance_template = create_lab_test_template()
- blood_test_template = create_blood_test_template(medical_department)
-
- patient_encounter = frappe.new_doc('Patient Encounter')
- patient_encounter.patient = patient
- patient_encounter.practitioner = create_practitioner()
- patient_encounter.encounter_date = getdate()
- patient_encounter.encounter_time = nowtime()
-
- tests = [insulin_resistance_template, blood_test_template]
- for entry in tests:
- patient_encounter.append('lab_test_prescription', {
- 'lab_test_code': entry.item,
- 'lab_test_name': entry.lab_test_name
- })
-
- patient_encounter.submit()
- return patient_encounter
-
-
-def create_practitioner():
- practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
-
- if not practitioner:
- practitioner = frappe.new_doc('Healthcare Practitioner')
- practitioner.first_name = '_Test Healthcare Practitioner'
- practitioner.gender = 'Female'
- practitioner.op_consulting_charge = 500
- practitioner.inpatient_visit_charge = 500
- practitioner.save(ignore_permissions=True)
- practitioner = practitioner.name
-
- return practitioner
diff --git a/erpnext/healthcare/doctype/lab_test_group_template/__init__.py b/erpnext/healthcare/doctype/lab_test_group_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/lab_test_group_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
deleted file mode 100644
index 2767f7e..0000000
--- a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
+++ /dev/null
@@ -1,119 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-03-29 17:37:29.913583",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
- "template_or_new_line",
- "lab_test_template",
- "lab_test_rate",
- "lab_test_description",
- "group_event",
- "group_test_uom",
- "secondary_uom",
- "conversion_factor",
- "allow_blank",
- "column_break_8",
- "group_test_normal_range"
- ],
- "fields": [
- {
- "default": "Add Test",
- "fieldname": "template_or_new_line",
- "fieldtype": "Select",
- "options": "Add Test\nAdd New Line",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "depends_on": "eval:doc.template_or_new_line == 'Add Test'",
- "fieldname": "lab_test_template",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Test Name",
- "options": "Lab Test Template"
- },
- {
- "fetch_from": "lab_test_template.lab_test_rate",
- "fieldname": "lab_test_rate",
- "fieldtype": "Currency",
- "label": "Rate",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "lab_test_template.lab_test_description",
- "fieldname": "lab_test_description",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Description",
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.template_or_new_line == 'Add New Line'",
- "fieldname": "group_event",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Event"
- },
- {
- "depends_on": "eval:doc.template_or_new_line =='Add New Line'",
- "fieldname": "group_test_uom",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "UOM",
- "options": "Lab Test UOM"
- },
- {
- "depends_on": "eval:doc.template_or_new_line == 'Add New Line'",
- "fieldname": "group_test_normal_range",
- "fieldtype": "Long Text",
- "ignore_xss_filter": 1,
- "label": "Normal Range"
- },
- {
- "fieldname": "column_break_8",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval:doc.template_or_new_line =='Add New Line'",
- "fieldname": "secondary_uom",
- "fieldtype": "Link",
- "label": "Secondary UOM",
- "options": "Lab Test UOM"
- },
- {
- "depends_on": "secondary_uom",
- "fieldname": "conversion_factor",
- "fieldtype": "Float",
- "label": "Conversion Factor",
- "mandatory_depends_on": "secondary_uom"
- },
- {
- "default": "0",
- "depends_on": "eval:doc.template_or_new_line == 'Add New Line'",
- "fieldname": "allow_blank",
- "fieldtype": "Check",
- "in_list_view": 1,
- "label": "Allow Blank"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-07-30 12:36:03.082391",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test Group Template",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.py b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.py
deleted file mode 100644
index 1e2cef4..0000000
--- a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class LabTestGroupTemplate(Document):
- pass
diff --git a/erpnext/healthcare/doctype/lab_test_sample/__init__.py b/erpnext/healthcare/doctype/lab_test_sample/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.js b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.js
deleted file mode 100644
index a5f4b4d..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Lab Test Sample', {
-});
diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json
deleted file mode 100644
index 2830038..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:sample",
- "beta": 1,
- "creation": "2016-04-04 17:35:44.823951",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
- "sample",
- "sample_uom"
- ],
- "fields": [
- {
- "fieldname": "sample",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Sample",
- "reqd": 1,
- "unique": 1
- },
- {
- "bold": 1,
- "fieldname": "sample_uom",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "UOM",
- "options": "Lab Test UOM"
- }
- ],
- "links": [],
- "modified": "2020-01-29 23:02:02.249839",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test Sample",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "share": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "sample",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.py b/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.py
deleted file mode 100644
index 4c66b72..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/lab_test_sample.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class LabTestSample(Document):
- pass
diff --git a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js
deleted file mode 100644
index ace60de..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test Sample", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Lab Test Sample
- () => frappe.tests.make('Lab Test Sample', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.py b/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.py
deleted file mode 100644
index 2bc56bd..0000000
--- a/erpnext/healthcare/doctype/lab_test_sample/test_lab_test_sample.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestLabTestSample(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/lab_test_template/__init__.py b/erpnext/healthcare/doctype/lab_test_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js
deleted file mode 100644
index 2e41f51..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2016, ESS
-// License: ESS license.txt
-
-frappe.ui.form.on('Lab Test Template', {
- lab_test_name: function(frm) {
- if (!frm.doc.lab_test_code)
- frm.set_value('lab_test_code', frm.doc.lab_test_name);
- if (!frm.doc.lab_test_description)
- frm.set_value('lab_test_description', frm.doc.lab_test_name);
- },
- refresh : function(frm) {
- // Restrict Special, Grouped type templates in Child Test Groups
- frm.set_query('lab_test_template', 'lab_test_groups', function() {
- return {
- filters: {
- lab_test_template_type: ['in', ['Single','Compound']]
- }
- };
- });
- },
- medical_code: function(frm) {
- frm.set_query('medical_code', function() {
- return {
- filters: {
- medical_code_standard: frm.doc.medical_code_standard
- }
- };
- });
- }
-});
-
-cur_frm.cscript.custom_refresh = function(doc) {
- cur_frm.set_df_property('lab_test_code', 'read_only', doc.__islocal ? 0 : 1);
-
- if (!doc.__islocal) {
- cur_frm.add_custom_button(__('Change Template Code'), function() {
- change_template_code(doc);
- });
- }
-};
-
-let change_template_code = function(doc) {
- let d = new frappe.ui.Dialog({
- title:__('Change Template Code'),
- fields:[
- {
- 'fieldtype': 'Data',
- 'label': 'Lab Test Template Code',
- 'fieldname': 'lab_test_code',
- reqd: 1
- }
- ],
- primary_action: function() {
- let values = d.get_values();
- if (values) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.lab_test_template.lab_test_template.change_test_code_from_template',
- 'args': {lab_test_code: values.lab_test_code, doc: doc},
- callback: function (data) {
- frappe.set_route('Form', 'Lab Test Template', data.message);
- }
- });
- }
- d.hide();
- },
- primary_action_label: __('Change Template Code')
- });
- d.show();
-
- d.set_values({
- 'lab_test_code': doc.lab_test_code
- });
-};
-
-frappe.ui.form.on('Lab Test Template', 'lab_test_name', function(frm) {
- frm.doc.change_in_item = 1;
-});
-
-frappe.ui.form.on('Lab Test Template', 'lab_test_rate', function(frm) {
- frm.doc.change_in_item = 1;
-});
-
-frappe.ui.form.on('Lab Test Template', 'lab_test_group', function(frm) {
- frm.doc.change_in_item = 1;
-});
-
-frappe.ui.form.on('Lab Test Template', 'lab_test_description', function(frm) {
- frm.doc.change_in_item = 1;
-});
-
-frappe.ui.form.on('Lab Test Groups', 'template_or_new_line', function (frm, cdt, cdn) {
- let child = locals[cdt][cdn];
- if (child.template_or_new_line == 'Add New Line') {
- frappe.model.set_value(cdt, cdn, 'lab_test_template', '');
- frappe.model.set_value(cdt, cdn, 'lab_test_description', '');
- }
-});
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
deleted file mode 100644
index c3fc842..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
+++ /dev/null
@@ -1,356 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:lab_test_code",
- "beta": 1,
- "creation": "2016-03-29 17:35:36.761223",
- "doctype": "DocType",
- "engine": "InnoDB",
- "field_order": [
- "lab_test_name",
- "item",
- "lab_test_code",
- "lab_test_group",
- "department",
- "column_break_3",
- "disabled",
- "lab_test_template_type",
- "is_billable",
- "lab_test_rate",
- "section_break_description",
- "lab_test_description",
- "section_break_normal",
- "lab_test_uom",
- "secondary_uom",
- "conversion_factor",
- "column_break_10",
- "lab_test_normal_range",
- "section_break_compound",
- "normal_test_templates",
- "section_break_special",
- "sensitivity",
- "descriptive_test_templates",
- "section_break_group",
- "lab_test_groups",
- "sb_sample_collection",
- "sample",
- "sample_uom",
- "sample_qty",
- "column_break_33",
- "sample_details",
- "medical_coding_section",
- "medical_code",
- "medical_code_standard",
- "worksheet_section",
- "worksheet_instructions",
- "result_legend_section",
- "legend_print_position",
- "result_legend",
- "change_in_item"
- ],
- "fields": [
- {
- "fieldname": "lab_test_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Test Name",
- "no_copy": 1,
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "item",
- "fieldtype": "Link",
- "label": "Item",
- "no_copy": 1,
- "options": "Item",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "lab_test_code",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Item Code",
- "no_copy": 1,
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "lab_test_group",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Item Group",
- "options": "Item Group",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "department",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Department",
- "options": "Medical Department",
- "reqd": 1
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "description": "<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",
- "fieldname": "lab_test_template_type",
- "fieldtype": "Select",
- "in_standard_filter": 1,
- "label": "Result Format",
- "options": "\nSingle\nCompound\nDescriptive\nGrouped\nNo Result"
- },
- {
- "default": "1",
- "depends_on": "eval:doc.lab_test_template_type != 'Grouped'",
- "description": "If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ",
- "fieldname": "is_billable",
- "fieldtype": "Check",
- "label": "Is Billable",
- "search_index": 1
- },
- {
- "depends_on": "eval:doc.is_billable == 1",
- "description": "This value is updated in the Default Sales Price List.",
- "fieldname": "lab_test_rate",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Rate",
- "mandatory_depends_on": "eval:doc.is_billable == 1"
- },
- {
- "collapsible": 1,
- "fieldname": "medical_coding_section",
- "fieldtype": "Section Break",
- "label": "Medical Coding"
- },
- {
- "depends_on": "medical_code_standard",
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "label": "Medical Code",
- "options": "Medical Code"
- },
- {
- "fieldname": "medical_code_standard",
- "fieldtype": "Link",
- "label": "Medical Code Standard",
- "options": "Medical Code Standard"
- },
- {
- "depends_on": "eval:doc.lab_test_template_type == 'Single'",
- "fieldname": "section_break_normal",
- "fieldtype": "Section Break",
- "label": "Lab Routine"
- },
- {
- "fieldname": "lab_test_uom",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "UOM",
- "options": "Lab Test UOM"
- },
- {
- "fieldname": "lab_test_normal_range",
- "fieldtype": "Long Text",
- "ignore_xss_filter": 1,
- "label": "Normal Range"
- },
- {
- "fieldname": "column_break_10",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval:doc.lab_test_template_type == 'Compound'",
- "fieldname": "section_break_compound",
- "fieldtype": "Section Break",
- "label": "Compound"
- },
- {
- "fieldname": "normal_test_templates",
- "fieldtype": "Table",
- "options": "Normal Test Template"
- },
- {
- "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'",
- "fieldname": "section_break_special",
- "fieldtype": "Section Break",
- "label": "Descriptive Test"
- },
- {
- "default": "0",
- "fieldname": "sensitivity",
- "fieldtype": "Check",
- "label": "Sensitivity"
- },
- {
- "depends_on": "eval:doc.lab_test_template_type == 'Grouped'",
- "fieldname": "section_break_group",
- "fieldtype": "Section Break",
- "label": "Group Tests"
- },
- {
- "fieldname": "lab_test_groups",
- "fieldtype": "Table",
- "options": "Lab Test Group Template"
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_description",
- "fieldtype": "Section Break",
- "label": "Description "
- },
- {
- "fieldname": "lab_test_description",
- "fieldtype": "Text Editor",
- "ignore_xss_filter": 1,
- "label": "Description",
- "no_copy": 1
- },
- {
- "fieldname": "sb_sample_collection",
- "fieldtype": "Section Break",
- "label": "Sample Collection"
- },
- {
- "fieldname": "sample",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Sample",
- "options": "Lab Test Sample"
- },
- {
- "fetch_from": "sample.sample_uom",
- "fieldname": "sample_uom",
- "fieldtype": "Data",
- "label": "UOM",
- "read_only": 1
- },
- {
- "default": "0",
- "fieldname": "change_in_item",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Change In Item",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "default": "0",
- "fieldname": "disabled",
- "fieldtype": "Check",
- "label": "Disabled"
- },
- {
- "default": "0",
- "fieldname": "sample_qty",
- "fieldtype": "Float",
- "label": "Quantity"
- },
- {
- "fieldname": "sample_details",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Collection Details"
- },
- {
- "collapsible": 1,
- "description": "Information to help easily interpret the test report, will be printed as part of the Lab Test result.",
- "fieldname": "result_legend_section",
- "fieldtype": "Section Break",
- "label": "Result Legend Print"
- },
- {
- "fieldname": "result_legend",
- "fieldtype": "Text Editor",
- "label": "Result Legend"
- },
- {
- "fieldname": "legend_print_position",
- "fieldtype": "Select",
- "label": "Print Position",
- "options": "Bottom\nTop\nBoth"
- },
- {
- "fieldname": "secondary_uom",
- "fieldtype": "Link",
- "label": "Secondary UOM",
- "options": "Lab Test UOM"
- },
- {
- "depends_on": "secondary_uom",
- "fieldname": "conversion_factor",
- "fieldtype": "Float",
- "label": "Conversion Factor",
- "mandatory_depends_on": "secondary_uom"
- },
- {
- "description": "Instructions to be printed on the worksheet",
- "fieldname": "worksheet_instructions",
- "fieldtype": "Text Editor",
- "label": "Worksheet Instructions"
- },
- {
- "collapsible": 1,
- "fieldname": "worksheet_section",
- "fieldtype": "Section Break",
- "label": "Worksheet Print"
- },
- {
- "fieldname": "descriptive_test_templates",
- "fieldtype": "Table",
- "options": "Descriptive Test Template"
- },
- {
- "fieldname": "column_break_33",
- "fieldtype": "Column Break"
- }
- ],
- "links": [],
- "modified": "2020-07-30 14:32:40.449818",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test Template",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "share": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "lab_test_code,lab_test_name,lab_test_template_type",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "lab_test_name",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
deleted file mode 100644
index 543dee2..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe, json
-from frappe.model.document import Document
-from frappe.model.rename_doc import rename_doc
-from frappe import _
-
-class LabTestTemplate(Document):
- def after_insert(self):
- if not self.item:
- create_item_from_template(self)
-
- def validate(self):
- if self.is_billable and (not self.lab_test_rate or self.lab_test_rate <= 0.0):
- frappe.throw(_('Standard Selling Rate should be greater than zero.'))
-
- self.validate_conversion_factor()
- self.enable_disable_item()
-
- def on_update(self):
- # If change_in_item update Item and Price List
- if self.change_in_item and self.is_billable and self.item:
- self.update_item()
- item_price = self.item_price_exists()
- if not item_price:
- if self.lab_test_rate and self.lab_test_rate > 0.0:
- price_list_name = frappe.db.get_value('Price List', {'selling': 1})
- make_item_price(self.lab_test_code, price_list_name, self.lab_test_rate)
- else:
- frappe.db.set_value('Item Price', item_price, 'price_list_rate', self.lab_test_rate)
-
- self.db_set('change_in_item', 0)
-
- elif not self.is_billable and self.item:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
-
- self.reload()
-
- def on_trash(self):
- # Remove template reference from item and disable item
- if self.item:
- try:
- item = self.item
- self.db_set('item', '')
- frappe.delete_doc('Item', item)
- except Exception:
- frappe.throw(_('Not permitted. Please disable the Lab Test Template'))
-
- def enable_disable_item(self):
- if self.is_billable:
- if self.disabled:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
- else:
- frappe.db.set_value('Item', self.item, 'disabled', 0)
-
- def update_item(self):
- item = frappe.get_doc('Item', self.item)
- if item:
- item.update({
- 'item_name': self.lab_test_name,
- 'item_group': self.lab_test_group,
- 'disabled': 0,
- 'standard_rate': self.lab_test_rate,
- 'description': self.lab_test_description
- })
- item.flags.ignore_mandatory = True
- item.save(ignore_permissions=True)
-
- def item_price_exists(self):
- item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code})
- if item_price:
- return item_price[0][0]
- return False
-
- def validate_conversion_factor(self):
- if self.lab_test_template_type == 'Single' and self.secondary_uom and not self.conversion_factor:
- frappe.throw(_('Conversion Factor is mandatory'))
- if self.lab_test_template_type == 'Compound':
- for item in self.normal_test_templates:
- if item.secondary_uom and not item.conversion_factor:
- frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(item.idx))
- if self.lab_test_template_type == 'Grouped':
- for group in self.lab_test_groups:
- if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor:
- frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(group.idx))
-
-
-def create_item_from_template(doc):
- uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
- # Insert item
- item = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': doc.lab_test_code,
- 'item_name':doc.lab_test_name,
- 'item_group': doc.lab_test_group,
- 'description':doc.lab_test_description,
- 'is_sales_item': 1,
- 'is_service_item': 1,
- 'is_purchase_item': 0,
- 'is_stock_item': 0,
- 'include_item_in_manufacturing': 0,
- 'show_in_website': 0,
- 'is_pro_applicable': 0,
- 'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled,
- 'stock_uom': uom
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- # Insert item price
- if doc.is_billable and doc.lab_test_rate != 0.0:
- price_list_name = frappe.db.get_value('Price List', {'selling': 1})
- if doc.lab_test_rate:
- make_item_price(item.name, price_list_name, doc.lab_test_rate)
- else:
- make_item_price(item.name, price_list_name, 0.0)
- # Set item in the template
- frappe.db.set_value('Lab Test Template', doc.name, 'item', item.name)
-
- doc.reload()
-
-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.whitelist()
-def change_test_code_from_template(lab_test_code, doc):
- doc = frappe._dict(json.loads(doc))
-
- if frappe.db.exists({'doctype': 'Item', 'item_code': lab_test_code}):
- frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code))
- else:
- rename_doc('Item', doc.name, lab_test_code, ignore_permissions=True)
- frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_code', lab_test_code)
- frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_name', lab_test_code)
- rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions=True)
- return lab_test_code
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py
deleted file mode 100644
index 94dfeea..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'template',
- 'transactions': [
- {
- 'label': _('Lab Tests'),
- 'items': ['Lab Test']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
deleted file mode 100644
index 08fc2cd..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
-(c) ESS 2015-16
-*/
-frappe.listview_settings['Lab Test Template'] = {
- add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'],
- filters: [['disabled', '=', 'No']]
-};
diff --git a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js
deleted file mode 100644
index 7c2ec8c..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Lab Test Template
- () => frappe.tests.make('Lab Test Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.py
deleted file mode 100644
index 4c9f55a..0000000
--- a/erpnext/healthcare/doctype/lab_test_template/test_lab_test_template.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-# test_records = frappe.get_test_records('Lab Test Template')
-
-class TestLabTestTemplate(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/lab_test_uom/__init__.py b/erpnext/healthcare/doctype/lab_test_uom/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.js b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.js
deleted file mode 100644
index 2107e79..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, ESS and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Lab Test UOM', {
-});
diff --git a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.json b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.json
deleted file mode 100644
index a6d5224..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.json
+++ /dev/null
@@ -1,148 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:lab_test_uom",
- "beta": 1,
- "creation": "2016-03-29 17:28:08.630148",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "lab_test_uom",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Lab Test UOM",
- "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": "uom_description",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "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": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-09-04 11:02:53.202718",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test UOM",
- "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": "Healthcare Administrator",
- "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": "Laboratory User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "lab_test_uom",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "lab_test_uom",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.py b/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.py
deleted file mode 100644
index 7ce8d2d..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/lab_test_uom.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class LabTestUOM(Document):
- pass
diff --git a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js
deleted file mode 100644
index 1328dda..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Lab Test UOM", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Lab Test UOM
- () => frappe.tests.make('Lab Test UOM', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.py b/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.py
deleted file mode 100644
index 0b3f516..0000000
--- a/erpnext/healthcare/doctype/lab_test_uom/test_lab_test_uom.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-# test_records = frappe.get_test_records('Lab Test UOM')
-
-class TestLabTestUOM(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/medical_code/__init__.py b/erpnext/healthcare/doctype/medical_code/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/medical_code/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.js b/erpnext/healthcare/doctype/medical_code/medical_code.js
deleted file mode 100644
index 0422d77..0000000
--- a/erpnext/healthcare/doctype/medical_code/medical_code.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Medical Code', {
-});
diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.json b/erpnext/healthcare/doctype/medical_code/medical_code.json
deleted file mode 100644
index 5d69830..0000000
--- a/erpnext/healthcare/doctype/medical_code/medical_code.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "allow_rename": 1,
- "beta": 1,
- "creation": "2017-06-21 13:02:56.122897",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "medical_code_standard",
- "code",
- "description"
- ],
- "fields": [
- {
- "fieldname": "medical_code_standard",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Medical Code Standard",
- "options": "Medical Code Standard",
- "reqd": 1
- },
- {
- "fieldname": "code",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Code",
- "reqd": 1,
- "unique": 1
- },
- {
- "bold": 1,
- "fieldname": "description",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Description"
- }
- ],
- "links": [],
- "modified": "2020-06-29 14:02:30.980032",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Medical Code",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "search_fields": "code, description",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.py b/erpnext/healthcare/doctype/medical_code/medical_code.py
deleted file mode 100644
index 0deaac3..0000000
--- a/erpnext/healthcare/doctype/medical_code/medical_code.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class MedicalCode(Document):
- def autoname(self):
- self.name = self.medical_code_standard+" "+self.code
diff --git a/erpnext/healthcare/doctype/medical_code/test_medical_code.js b/erpnext/healthcare/doctype/medical_code/test_medical_code.js
deleted file mode 100644
index 8cc7c40..0000000
--- a/erpnext/healthcare/doctype/medical_code/test_medical_code.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Medical Code", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Medical Code
- () => frappe.tests.make('Medical Code', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/medical_code/test_medical_code.py b/erpnext/healthcare/doctype/medical_code/test_medical_code.py
deleted file mode 100644
index b1e0402..0000000
--- a/erpnext/healthcare/doctype/medical_code/test_medical_code.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestMedicalCode(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/medical_code_standard/__init__.py b/erpnext/healthcare/doctype/medical_code_standard/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.js b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.js
deleted file mode 100644
index 4bf6d3e..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Medical Code Standard', {
-});
diff --git a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.json b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.json
deleted file mode 100644
index 886938d..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:medical_code",
- "beta": 1,
- "creation": "2017-06-21 13:07:00.463176",
- "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": "medical_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": "Medical 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
- }
- ],
- "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-08-31 14:15:40.820693",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Medical Code Standard",
- "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": "Physician",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "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/healthcare/doctype/medical_code_standard/medical_code_standard.py b/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.py
deleted file mode 100644
index 7b2731c..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/medical_code_standard.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class MedicalCodeStandard(Document):
- pass
diff --git a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js
deleted file mode 100644
index 6ab6d53..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Medical Code Standard", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Medical Code Standard
- () => frappe.tests.make('Medical Code Standard', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.py b/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.py
deleted file mode 100644
index fde095d..0000000
--- a/erpnext/healthcare/doctype/medical_code_standard/test_medical_code_standard.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestMedicalCodeStandard(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/medical_department/__init__.py b/erpnext/healthcare/doctype/medical_department/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/medical_department/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/medical_department/medical_department.js b/erpnext/healthcare/doctype/medical_department/medical_department.js
deleted file mode 100644
index 25aeeb8..0000000
--- a/erpnext/healthcare/doctype/medical_department/medical_department.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Medical Department', {
-});
diff --git a/erpnext/healthcare/doctype/medical_department/medical_department.json b/erpnext/healthcare/doctype/medical_department/medical_department.json
deleted file mode 100644
index 40f14ca..0000000
--- a/erpnext/healthcare/doctype/medical_department/medical_department.json
+++ /dev/null
@@ -1,156 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:department",
- "beta": 1,
- "creation": "2017-02-27 13:38:30.806362",
- "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": "department",
- "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": "Department",
- "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-08-31 13:41:59.611698",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Medical Department",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 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": "Physician",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "apply_user_permissions": 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": "Nursing User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "apply_user_permissions": 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": "Laboratory 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": "Healthcare Administrator",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "department",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "department",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/medical_department/medical_department.py b/erpnext/healthcare/doctype/medical_department/medical_department.py
deleted file mode 100644
index 0f2d4fc..0000000
--- a/erpnext/healthcare/doctype/medical_department/medical_department.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class MedicalDepartment(Document):
- pass
diff --git a/erpnext/healthcare/doctype/medical_department/test_medical_department.js b/erpnext/healthcare/doctype/medical_department/test_medical_department.js
deleted file mode 100644
index fdf4971..0000000
--- a/erpnext/healthcare/doctype/medical_department/test_medical_department.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Medical Department", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Medical Department
- () => frappe.tests.make('Medical Department', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/medical_department/test_medical_department.py b/erpnext/healthcare/doctype/medical_department/test_medical_department.py
deleted file mode 100644
index 543750a..0000000
--- a/erpnext/healthcare/doctype/medical_department/test_medical_department.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-# test_records = frappe.get_test_records('Medical Department')
-
-class TestMedicalDepartment(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/normal_test_result/__init__.py b/erpnext/healthcare/doctype/normal_test_result/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/normal_test_result/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/normal_test_result/normal_test_result.json b/erpnext/healthcare/doctype/normal_test_result/normal_test_result.json
deleted file mode 100644
index c8f43d3..0000000
--- a/erpnext/healthcare/doctype/normal_test_result/normal_test_result.json
+++ /dev/null
@@ -1,186 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-02-22 15:06:08.295224",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "lab_test_name",
- "lab_test_event",
- "result_value",
- "lab_test_uom",
- "secondary_uom_result",
- "secondary_uom",
- "conversion_factor",
- "column_break_10",
- "allow_blank",
- "normal_range",
- "lab_test_comment",
- "bold",
- "italic",
- "underline",
- "template",
- "require_result_value"
- ],
- "fields": [
- {
- "fieldname": "lab_test_name",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Test Name",
- "read_only": 1
- },
- {
- "fieldname": "lab_test_event",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Event",
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "result_value",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Result Value"
- },
- {
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "lab_test_uom",
- "fieldtype": "Link",
- "label": "UOM",
- "options": "Lab Test UOM",
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "normal_range",
- "fieldtype": "Long Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Normal Range",
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "lab_test_comment",
- "fieldtype": "Data",
- "hidden": 1,
- "in_list_view": 1,
- "label": "Comment",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "template",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Template",
- "options": "Lab Test Template",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "secondary_uom",
- "fieldtype": "Link",
- "label": "Secondary UOM",
- "options": "Lab Test UOM",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "depends_on": "secondary_uom",
- "fieldname": "conversion_factor",
- "fieldtype": "Float",
- "label": "Conversion Factor",
- "mandatory_depends_on": "secondary_uom",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.require_result_value && doc.result_value",
- "fieldname": "secondary_uom_result",
- "fieldtype": "Data",
- "label": "Secondary UOM Result",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- },
- {
- "allow_on_submit": 1,
- "default": "0",
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "bold",
- "fieldtype": "Check",
- "label": "Bold",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "allow_on_submit": 1,
- "default": "0",
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "italic",
- "fieldtype": "Check",
- "label": "Italic",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "allow_on_submit": 1,
- "default": "0",
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "underline",
- "fieldtype": "Check",
- "label": "Underline",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "column_break_10",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "require_result_value",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Require Result Value",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "default": "1",
- "depends_on": "eval:doc.require_result_value",
- "fieldname": "allow_blank",
- "fieldtype": "Check",
- "label": "Allow Blank",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-07-08 16:03:17.522893",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Normal Test Result",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/normal_test_result/normal_test_result.py b/erpnext/healthcare/doctype/normal_test_result/normal_test_result.py
deleted file mode 100644
index 63abf02..0000000
--- a/erpnext/healthcare/doctype/normal_test_result/normal_test_result.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class NormalTestResult(Document):
- pass
diff --git a/erpnext/healthcare/doctype/normal_test_template/__init__.py b/erpnext/healthcare/doctype/normal_test_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/normal_test_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/normal_test_template/normal_test_template.json b/erpnext/healthcare/doctype/normal_test_template/normal_test_template.json
deleted file mode 100644
index 8dd6476..0000000
--- a/erpnext/healthcare/doctype/normal_test_template/normal_test_template.json
+++ /dev/null
@@ -1,84 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2016-02-22 16:09:54.310628",
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "heading_text",
- "lab_test_event",
- "allow_blank",
- "lab_test_uom",
- "secondary_uom",
- "conversion_factor",
- "column_break_5",
- "normal_range"
- ],
- "fields": [
- {
- "fieldname": "heading_text",
- "fieldtype": "Heading",
- "ignore_xss_filter": 1,
- "label": "Test"
- },
- {
- "fieldname": "lab_test_event",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Event"
- },
- {
- "fieldname": "lab_test_uom",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "UOM",
- "options": "Lab Test UOM"
- },
- {
- "fieldname": "normal_range",
- "fieldtype": "Long Text",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Normal Range"
- },
- {
- "fieldname": "column_break_5",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "secondary_uom",
- "fieldtype": "Link",
- "label": "Secondary UOM",
- "options": "Lab Test UOM"
- },
- {
- "depends_on": "secondary_uom",
- "fieldname": "conversion_factor",
- "fieldtype": "Float",
- "label": "Conversion Factor",
- "mandatory_depends_on": "secondary_uom"
- },
- {
- "default": "0",
- "fieldname": "allow_blank",
- "fieldtype": "Check",
- "label": "Allow Blank"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-06-23 13:28:40.156224",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Normal Test Template",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/normal_test_template/normal_test_template.py b/erpnext/healthcare/doctype/normal_test_template/normal_test_template.py
deleted file mode 100644
index bc2c991..0000000
--- a/erpnext/healthcare/doctype/normal_test_template/normal_test_template.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class NormalTestTemplate(Document):
- pass
diff --git a/erpnext/healthcare/doctype/organism/__init__.py b/erpnext/healthcare/doctype/organism/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/organism/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/organism/organism.js b/erpnext/healthcare/doctype/organism/organism.js
deleted file mode 100644
index fbcb094..0000000
--- a/erpnext/healthcare/doctype/organism/organism.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Organism', {
-});
diff --git a/erpnext/healthcare/doctype/organism/organism.json b/erpnext/healthcare/doctype/organism/organism.json
deleted file mode 100644
index 88a7686..0000000
--- a/erpnext/healthcare/doctype/organism/organism.json
+++ /dev/null
@@ -1,152 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "field:organism",
- "beta": 1,
- "creation": "2019-09-06 16:29:07.797960",
- "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_if_empty": 0,
- "fieldname": "organism",
- "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": "Organism",
- "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,
- "fetch_if_empty": 0,
- "fieldname": "abbr",
- "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": "Abbr",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 1
- }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-10-04 19:45:33.353753",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Organism",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "organism, abbr",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "organism",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/organism/organism.py b/erpnext/healthcare/doctype/organism/organism.py
deleted file mode 100644
index 1ead762..0000000
--- a/erpnext/healthcare/doctype/organism/organism.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class Organism(Document):
- pass
diff --git a/erpnext/healthcare/doctype/organism/test_organism.js b/erpnext/healthcare/doctype/organism/test_organism.js
deleted file mode 100644
index d57e553..0000000
--- a/erpnext/healthcare/doctype/organism/test_organism.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Organism", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Organism
- () => frappe.tests.make('Organism', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/organism/test_organism.py b/erpnext/healthcare/doctype/organism/test_organism.py
deleted file mode 100644
index ecb9665..0000000
--- a/erpnext/healthcare/doctype/organism/test_organism.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestOrganism(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/organism_test_item/organism_test_item.json b/erpnext/healthcare/doctype/organism_test_item/organism_test_item.json
deleted file mode 100644
index 56d0a4d..0000000
--- a/erpnext/healthcare/doctype/organism_test_item/organism_test_item.json
+++ /dev/null
@@ -1,144 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2019-09-06 16:37:59.698996",
- "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_if_empty": 0,
- "fieldname": "organism",
- "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": "Organism",
- "length": 0,
- "no_copy": 0,
- "options": "Organism",
- "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_if_empty": 0,
- "fieldname": "colony_population",
- "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": "Colony Population",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "colony_uom",
- "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": "Colony UOM",
- "length": 0,
- "no_copy": 0,
- "options": "Lab Test UOM",
- "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": "2019-10-04 19:48:04.104234",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Organism Test Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "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/healthcare/doctype/organism_test_item/organism_test_item.py b/erpnext/healthcare/doctype/organism_test_item/organism_test_item.py
deleted file mode 100644
index 019a55b..0000000
--- a/erpnext/healthcare/doctype/organism_test_item/organism_test_item.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class OrganismTestItem(Document):
- pass
diff --git a/erpnext/healthcare/doctype/organism_test_result/__init__.py b/erpnext/healthcare/doctype/organism_test_result/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/organism_test_result/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/organism_test_result/organism_test_result.json b/erpnext/healthcare/doctype/organism_test_result/organism_test_result.json
deleted file mode 100644
index 8b238de..0000000
--- a/erpnext/healthcare/doctype/organism_test_result/organism_test_result.json
+++ /dev/null
@@ -1,144 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2019-09-06 16:37:59.698996",
- "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_if_empty": 0,
- "fieldname": "organism",
- "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": "Organism",
- "length": 0,
- "no_copy": 0,
- "options": "Organism",
- "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_if_empty": 0,
- "fieldname": "colony_population",
- "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": "Colony Population",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "colony_uom",
- "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": "Colony UOM",
- "length": 0,
- "no_copy": 0,
- "options": "Lab Test UOM",
- "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": "2019-10-04 19:48:04.104234",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Organism Test Result",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "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/healthcare/doctype/organism_test_result/organism_test_result.py b/erpnext/healthcare/doctype/organism_test_result/organism_test_result.py
deleted file mode 100644
index 02393c2..0000000
--- a/erpnext/healthcare/doctype/organism_test_result/organism_test_result.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class OrganismTestResult(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js
deleted file mode 100644
index bce42e5..0000000
--- a/erpnext/healthcare/doctype/patient/patient.js
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient', {
- refresh: function (frm) {
- frm.set_query('patient', 'patient_relation', function () {
- return {
- filters: [
- ['Patient', 'name', '!=', frm.doc.name]
- ]
- };
- });
- frm.set_query('customer_group', {'is_group': 0});
- frm.set_query('default_price_list', { 'selling': 1});
-
- if (frappe.defaults.get_default('patient_name_by') != 'Naming Series') {
- frm.toggle_display('naming_series', false);
- } else {
- erpnext.toggle_naming_series();
- }
-
- if (frappe.defaults.get_default('collect_registration_fee') && frm.doc.status == 'Disabled') {
- frm.add_custom_button(__('Invoice Patient Registration'), function () {
- invoice_registration(frm);
- });
- }
-
- if (frm.doc.patient_name && frappe.user.has_role('Physician')) {
- frm.add_custom_button(__('Patient History'), function() {
- frappe.route_options = {'patient': frm.doc.name};
- frappe.set_route('patient_history');
- },'View');
- }
-
- if (!frm.doc.__islocal && (frappe.user.has_role('Nursing User') || frappe.user.has_role('Physician'))) {
- frm.add_custom_button(__('Vital Signs'), function () {
- create_vital_signs(frm);
- }, 'Create');
- frm.add_custom_button(__('Medical Record'), function () {
- create_medical_record(frm);
- }, 'Create');
- frm.add_custom_button(__('Patient Encounter'), function () {
- create_encounter(frm);
- }, 'Create');
- frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option
- }
- },
- onload: function (frm) {
- if (!frm.doc.dob) {
- $(frm.fields_dict['age_html'].wrapper).html('');
- }
- if (frm.doc.dob) {
- $(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${get_age(frm.doc.dob)}`);
- }
- }
-});
-
-frappe.ui.form.on('Patient', 'dob', function(frm) {
- if (frm.doc.dob) {
- let today = new Date();
- let birthDate = new Date(frm.doc.dob);
- if (today < birthDate){
- frappe.msgprint(__('Please select a valid Date'));
- frappe.model.set_value(frm.doctype,frm.docname, 'dob', '');
- }
- else {
- let age_str = get_age(frm.doc.dob);
- $(frm.fields_dict['age_html'].wrapper).html(`${__('AGE')} : ${age_str}`);
- }
- }
- else {
- $(frm.fields_dict['age_html'].wrapper).html('');
- }
-});
-
-frappe.ui.form.on('Patient Relation', {
- patient_relation_add: function(frm){
- frm.fields_dict['patient_relation'].grid.get_field('patient').get_query = function(doc){
- let patient_list = [];
- if(!doc.__islocal) patient_list.push(doc.name);
- $.each(doc.patient_relation, function(idx, val){
- if (val.patient) patient_list.push(val.patient);
- });
- return { filters: [['Patient', 'name', 'not in', patient_list]] };
- };
- }
-});
-
-let create_medical_record = function (frm) {
- frappe.route_options = {
- 'patient': frm.doc.name,
- 'status': 'Open',
- 'reference_doctype': 'Patient Medical Record',
- 'reference_owner': frm.doc.owner
- };
- frappe.new_doc('Patient Medical Record');
-};
-
-let get_age = function (birth) {
- let ageMS = Date.parse(Date()) - Date.parse(birth);
- let age = new Date();
- age.setTime(ageMS);
- let years = age.getFullYear() - 1970;
- return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
-};
-
-let create_vital_signs = function (frm) {
- if (!frm.doc.name) {
- frappe.throw(__('Please save the patient first'));
- }
- frappe.route_options = {
- 'patient': frm.doc.name,
- };
- frappe.new_doc('Vital Signs');
-};
-
-let create_encounter = function (frm) {
- if (!frm.doc.name) {
- frappe.throw(__('Please save the patient first'));
- }
- frappe.route_options = {
- 'patient': frm.doc.name,
- };
- frappe.new_doc('Patient Encounter');
-};
-
-let invoice_registration = function (frm) {
- frappe.call({
- doc: frm.doc,
- method: 'invoice_patient_registration',
- callback: function(data) {
- if (!data.exc) {
- if (data.message.invoice) {
- frappe.set_route('Form', 'Sales Invoice', data.message.invoice);
- }
- cur_frm.reload_doc();
- }
- }
- });
-};
diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json
deleted file mode 100644
index 8af1a9c..0000000
--- a/erpnext/healthcare/doctype/patient/patient.json
+++ /dev/null
@@ -1,462 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2017-01-23 14:03:49.084370",
- "description": "Patient",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
- "basic_info",
- "naming_series",
- "first_name",
- "middle_name",
- "last_name",
- "patient_name",
- "sex",
- "blood_group",
- "dob",
- "age_html",
- "image",
- "column_break_14",
- "status",
- "inpatient_record",
- "inpatient_status",
- "report_preference",
- "mobile",
- "email",
- "phone",
- "customer_details_section",
- "customer",
- "customer_group",
- "territory",
- "column_break_24",
- "default_currency",
- "default_price_list",
- "language",
- "personal_and_social_history",
- "occupation",
- "column_break_25",
- "marital_status",
- "sb_relation",
- "patient_relation",
- "allergy_medical_and_surgical_history",
- "allergies",
- "medication",
- "column_break_20",
- "medical_history",
- "surgical_history",
- "risk_factors",
- "tobacco_past_use",
- "tobacco_current_use",
- "alcohol_past_use",
- "alcohol_current_use",
- "column_break_32",
- "surrounding_factors",
- "other_risk_factors",
- "more_info",
- "patient_details"
- ],
- "fields": [
- {
- "fieldname": "basic_info",
- "fieldtype": "Section Break",
- "label": "Patient Demographics",
- "oldfieldtype": "Section Break",
- "options": "fa fa-user"
- },
- {
- "fieldname": "inpatient_status",
- "fieldtype": "Select",
- "in_preview": 1,
- "label": "Inpatient Status",
- "options": "\nAdmission Scheduled\nAdmitted\nDischarge Scheduled",
- "read_only": 1
- },
- {
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-PAT-.YYYY.-",
- "print_hide": 1,
- "report_hide": 1,
- "set_only_once": 1
- },
- {
- "bold": 1,
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "in_global_search": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Full Name",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "sex",
- "fieldtype": "Link",
- "in_preview": 1,
- "label": "Gender",
- "options": "Gender",
- "reqd": 1
- },
- {
- "bold": 1,
- "fieldname": "blood_group",
- "fieldtype": "Select",
- "in_preview": 1,
- "label": "Blood Group",
- "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative"
- },
- {
- "bold": 1,
- "fieldname": "dob",
- "fieldtype": "Date",
- "in_preview": 1,
- "label": "Date of birth"
- },
- {
- "fieldname": "age_html",
- "fieldtype": "HTML",
- "label": "Age",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Status",
- "no_copy": 1,
- "options": "Active\nDisabled",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "image",
- "fieldtype": "Attach Image",
- "hidden": 1,
- "in_preview": 1,
- "label": "Image",
- "no_copy": 1,
- "print_hide": 1,
- "width": "50%"
- },
- {
- "fieldname": "column_break_14",
- "fieldtype": "Column Break"
- },
- {
- "description": "If \"Link Customer to Patient\" is checked in Healthcare Settings and an existing Customer is not selected then, a Customer will be created for this Patient for recording transactions in Accounts module.",
- "fieldname": "customer",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Customer",
- "options": "Customer",
- "set_only_once": 1
- },
- {
- "fieldname": "report_preference",
- "fieldtype": "Select",
- "label": "Report Preference",
- "options": "\nEmail\nPrint"
- },
- {
- "bold": 1,
- "fieldname": "mobile",
- "fieldtype": "Data",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Mobile",
- "options": "Phone"
- },
- {
- "bold": 1,
- "fieldname": "email",
- "fieldtype": "Data",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Email",
- "options": "Email"
- },
- {
- "fieldname": "phone",
- "fieldtype": "Data",
- "in_filter": 1,
- "label": "Phone",
- "options": "Phone"
- },
- {
- "collapsible": 1,
- "fieldname": "sb_relation",
- "fieldtype": "Section Break",
- "label": "Patient Relation"
- },
- {
- "fieldname": "patient_relation",
- "fieldtype": "Table",
- "label": "Patient Relation",
- "options": "Patient Relation"
- },
- {
- "collapsible": 1,
- "fieldname": "allergy_medical_and_surgical_history",
- "fieldtype": "Section Break",
- "label": "Allergies, Medical and Surgical History"
- },
- {
- "fieldname": "allergies",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Allergies",
- "no_copy": 1
- },
- {
- "fieldname": "medication",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Medication"
- },
- {
- "fieldname": "column_break_20",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "medical_history",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Medical History"
- },
- {
- "fieldname": "surgical_history",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Surgical History"
- },
- {
- "collapsible": 1,
- "fieldname": "personal_and_social_history",
- "fieldtype": "Section Break",
- "label": "Personal and Social History"
- },
- {
- "fieldname": "occupation",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_standard_filter": 1,
- "label": "Occupation"
- },
- {
- "fieldname": "column_break_25",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "marital_status",
- "fieldtype": "Select",
- "label": "Marital Status",
- "options": "\nSingle\nMarried\nDivorced\nWidow"
- },
- {
- "collapsible": 1,
- "fieldname": "risk_factors",
- "fieldtype": "Section Break",
- "label": "Risk Factors"
- },
- {
- "fieldname": "tobacco_past_use",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "label": "Tobacco Consumption (Past)"
- },
- {
- "fieldname": "tobacco_current_use",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "label": "Tobacco Consumption (Present)"
- },
- {
- "fieldname": "alcohol_past_use",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "label": "Alcohol Consumption (Past)"
- },
- {
- "fieldname": "alcohol_current_use",
- "fieldtype": "Data",
- "ignore_user_permissions": 1,
- "label": "Alcohol Consumption (Present)"
- },
- {
- "fieldname": "column_break_32",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "surrounding_factors",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Occupational Hazards and Environmental Factors"
- },
- {
- "fieldname": "other_risk_factors",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Other Risk Factors"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "patient_details",
- "fieldname": "more_info",
- "fieldtype": "Section Break",
- "label": "More Information",
- "oldfieldtype": "Section Break",
- "options": "fa fa-file-text"
- },
- {
- "description": "Additional information regarding the patient",
- "fieldname": "patient_details",
- "fieldtype": "Text",
- "ignore_xss_filter": 1,
- "label": "Patient Details"
- },
- {
- "fieldname": "default_currency",
- "fieldtype": "Link",
- "label": "Billing Currency",
- "options": "Currency"
- },
- {
- "fieldname": "last_name",
- "fieldtype": "Data",
- "label": "Last Name"
- },
- {
- "fieldname": "first_name",
- "fieldtype": "Data",
- "label": "First Name",
- "oldfieldtype": "Data",
- "reqd": 1
- },
- {
- "fieldname": "middle_name",
- "fieldtype": "Data",
- "label": "Middle Name (optional)"
- },
- {
- "collapsible": 1,
- "fieldname": "customer_details_section",
- "fieldtype": "Section Break",
- "label": "Customer Details"
- },
- {
- "fieldname": "customer_group",
- "fieldtype": "Link",
- "label": "Customer Group",
- "options": "Customer Group"
- },
- {
- "fieldname": "territory",
- "fieldtype": "Link",
- "label": "Territory",
- "options": "Territory"
- },
- {
- "fieldname": "column_break_24",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "default_price_list",
- "fieldtype": "Link",
- "label": "Default Price List",
- "options": "Price List"
- },
- {
- "fieldname": "language",
- "fieldtype": "Link",
- "label": "Print Language",
- "options": "Language"
- }
- ],
- "icon": "fa fa-user",
- "image_field": "image",
- "links": [],
- "max_attachments": 50,
- "modified": "2020-04-25 17:24:32.146415",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient",
- "name_case": "Title Case",
- "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": "Physician",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient_name,mobile,email,phone",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "ASC",
- "title_field": "patient_name",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py
deleted file mode 100644
index 56a3400..0000000
--- a/erpnext/healthcare/doctype/patient/patient.py
+++ /dev/null
@@ -1,186 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cint, cstr, getdate
-import dateutil
-from frappe.model.naming import set_name_by_naming_series
-from frappe.utils.nestedset import get_root_of
-from erpnext import get_default_currency
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms
-
-class Patient(Document):
- def validate(self):
- self.set_full_name()
- self.add_as_website_user()
-
- def before_insert(self):
- self.set_missing_customer_details()
-
- def after_insert(self):
- self.add_as_website_user()
- self.reload()
- if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient') and not self.customer:
- create_customer(self)
- if frappe.db.get_single_value('Healthcare Settings', 'collect_registration_fee'):
- frappe.db.set_value('Patient', self.name, 'status', 'Disabled')
- else:
- send_registration_sms(self)
- self.reload() # self.notify_update()
-
- def on_update(self):
- if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'):
- if self.customer:
- customer = frappe.get_doc('Customer', self.customer)
- if self.customer_group:
- customer.customer_group = self.customer_group
- if self.territory:
- customer.territory = self.territory
- customer.customer_name = self.patient_name
- customer.default_price_list = self.default_price_list
- customer.default_currency = self.default_currency
- customer.language = self.language
- customer.ignore_mandatory = True
- customer.save(ignore_permissions=True)
- else:
- create_customer(self)
-
- def set_full_name(self):
- if self.last_name:
- self.patient_name = ' '.join(filter(None, [self.first_name, self.last_name]))
- else:
- self.patient_name = self.first_name
-
- def set_missing_customer_details(self):
- if not self.customer_group:
- self.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') or get_root_of('Customer Group')
- if not self.territory:
- self.territory = frappe.db.get_single_value('Selling Settings', 'territory') or get_root_of('Territory')
- if not self.default_price_list:
- self.default_price_list = frappe.db.get_single_value('Selling Settings', 'selling_price_list')
-
- if not self.customer_group or not self.territory or not self.default_price_list:
- frappe.msgprint(_('Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings'), alert=True)
-
- if not self.default_currency:
- self.default_currency = get_default_currency()
- if not self.language:
- self.language = frappe.db.get_single_value('System Settings', 'language')
-
- def add_as_website_user(self):
- if self.email:
- if not frappe.db.exists ('User', self.email):
- user = frappe.get_doc({
- 'doctype': 'User',
- 'first_name': self.first_name,
- 'last_name': self.last_name,
- 'email': self.email,
- 'user_type': 'Website User'
- })
- user.flags.ignore_permissions = True
- user.add_roles('Patient')
-
- def autoname(self):
- patient_name_by = frappe.db.get_single_value('Healthcare Settings', 'patient_name_by')
- if patient_name_by == 'Patient Name':
- self.name = self.get_patient_name()
- else:
- set_name_by_naming_series(self)
-
- def get_patient_name(self):
- self.set_full_name()
- name = self.patient_name
- if frappe.db.get_value('Patient', name):
- count = frappe.db.sql("""select ifnull(MAX(CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED)), 0) from tabPatient
- where name like %s""", "%{0} - %".format(name), as_list=1)[0][0]
- count = cint(count) + 1
- return "{0} - {1}".format(name, cstr(count))
-
- return name
-
- def get_age(self):
- age_str = ''
- if self.dob:
- dob = getdate(self.dob)
- age = dateutil.relativedelta.relativedelta(getdate(), dob)
- age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
- return age_str
-
- @frappe.whitelist()
- def invoice_patient_registration(self):
- if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'):
- company = frappe.defaults.get_user_default('company')
- if not company:
- company = frappe.db.get_single_value('Global Defaults', 'default_company')
-
- sales_invoice = make_invoice(self.name, company)
- sales_invoice.save(ignore_permissions=True)
- frappe.db.set_value('Patient', self.name, 'status', 'Active')
- send_registration_sms(self)
-
- return {'invoice': sales_invoice.name}
-
-def create_customer(doc):
- customer = frappe.get_doc({
- 'doctype': 'Customer',
- 'customer_name': doc.patient_name,
- 'customer_group': doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group'),
- 'territory' : doc.territory or frappe.db.get_single_value('Selling Settings', 'territory'),
- 'customer_type': 'Individual',
- 'default_currency': doc.default_currency,
- 'default_price_list': doc.default_price_list,
- 'language': doc.language
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- frappe.db.set_value('Patient', doc.name, 'customer', customer.name)
- frappe.msgprint(_('Customer {0} is created.').format(customer.name), alert=True)
-
-def make_invoice(patient, company):
- uom = frappe.db.exists('UOM', 'Nos') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
- sales_invoice = frappe.new_doc('Sales Invoice')
- sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer')
- sales_invoice.due_date = getdate()
- sales_invoice.company = company
- sales_invoice.is_pos = 0
- sales_invoice.debit_to = get_receivable_account(company)
-
- item_line = sales_invoice.append('items')
- item_line.item_name = 'Registeration Fee'
- item_line.description = 'Registeration Fee'
- item_line.qty = 1
- item_line.uom = uom
- item_line.conversion_factor = 1
- item_line.income_account = get_income_account(None, company)
- item_line.rate = frappe.db.get_single_value('Healthcare Settings', 'registration_fee')
- item_line.amount = item_line.rate
- sales_invoice.set_missing_values()
- return sales_invoice
-
-@frappe.whitelist()
-def get_patient_detail(patient):
- patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1)
- if not patient_dict:
- frappe.throw(_('Patient not found'))
- vital_sign = frappe.db.sql("""select * from `tabVital Signs` where patient=%s
- order by signs_date desc limit 1""", (patient), as_dict=1)
-
- details = patient_dict[0]
- if vital_sign:
- details.update(vital_sign[0])
- return details
-
-def get_timeline_data(doctype, name):
- """Return timeline data from medical records"""
- return dict(frappe.db.sql('''
- SELECT
- unix_timestamp(communication_date), count(*)
- FROM
- `tabPatient Medical Record`
- WHERE
- patient=%s
- and `communication_date` > date_sub(curdate(), interval 1 year)
- GROUP BY communication_date''', name))
diff --git a/erpnext/healthcare/doctype/patient/patient_dashboard.py b/erpnext/healthcare/doctype/patient/patient_dashboard.py
deleted file mode 100644
index 39603f7..0000000
--- a/erpnext/healthcare/doctype/patient/patient_dashboard.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'heatmap': True,
- 'heatmap_message': _('This is based on transactions against this Patient. See timeline below for details'),
- 'fieldname': 'patient',
- 'transactions': [
- {
- 'label': _('Appointments and Patient Encounters'),
- 'items': ['Patient Appointment', 'Patient Encounter']
- },
- {
- 'label': _('Lab Tests and Vital Signs'),
- 'items': ['Lab Test', 'Sample Collection', 'Vital Signs']
- },
- {
- 'label': _('Billing'),
- 'items': ['Sales Invoice']
- },
- {
- 'label': _('Orders'),
- 'items': ['Inpatient Medication Order']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/patient/test_patient.js b/erpnext/healthcare/doctype/patient/test_patient.js
deleted file mode 100644
index e1d9ecb..0000000
--- a/erpnext/healthcare/doctype/patient/test_patient.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Patient', [
- // insert a new Patient
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient/test_patient.py b/erpnext/healthcare/doctype/patient/test_patient.py
deleted file mode 100644
index 9274b6f..0000000
--- a/erpnext/healthcare/doctype/patient/test_patient.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-import frappe
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
-
-class TestPatient(unittest.TestCase):
- def test_customer_created(self):
- frappe.db.sql("""delete from `tabPatient`""")
- frappe.db.set_value('Healthcare Settings', None, 'link_customer_to_patient', 1)
- patient = create_patient()
- self.assertTrue(frappe.db.get_value('Patient', patient, 'customer'))
-
- def test_patient_registration(self):
- frappe.db.sql("""delete from `tabPatient`""")
- settings = frappe.get_single('Healthcare Settings')
- settings.collect_registration_fee = 1
- settings.registration_fee = 500
- settings.save()
-
- patient = create_patient()
- patient = frappe.get_doc('Patient', patient)
- self.assertEqual(patient.status, 'Disabled')
-
- # check sales invoice and patient status
- result = patient.invoice_patient_registration()
- self.assertTrue(frappe.db.exists('Sales Invoice', result.get('invoice')))
- self.assertTrue(patient.status, 'Active')
-
- settings.collect_registration_fee = 0
- settings.save()
diff --git a/erpnext/healthcare/doctype/patient_appointment/__init__.py b/erpnext/healthcare/doctype/patient_appointment/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
deleted file mode 100644
index 2976ef1..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ /dev/null
@@ -1,600 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-frappe.provide('erpnext.queries');
-frappe.ui.form.on('Patient Appointment', {
- setup: function(frm) {
- frm.custom_make_buttons = {
- 'Vital Signs': 'Vital Signs',
- 'Patient Encounter': 'Patient Encounter'
- };
- },
-
- onload: function(frm) {
- if (frm.is_new()) {
- frm.set_value('appointment_time', null);
- frm.disable_save();
- }
- },
-
- refresh: function(frm) {
- frm.set_query('patient', function () {
- return {
- filters: {'status': 'Active'}
- };
- });
-
- frm.set_query('practitioner', function() {
- if (frm.doc.department) {
- return {
- filters: {
- 'department': frm.doc.department
- }
- };
- }
- });
-
- frm.set_query('service_unit', function() {
- return {
- query: 'erpnext.controllers.queries.get_healthcare_service_units',
- filters: {
- company: frm.doc.company,
- inpatient_record: frm.doc.inpatient_record
- }
- };
- });
-
- frm.set_query('therapy_plan', function() {
- return {
- filters: {
- 'patient': frm.doc.patient
- }
- };
- });
-
- frm.trigger('set_therapy_type_filter');
-
- if (frm.is_new()) {
- frm.page.set_primary_action(__('Check Availability'), function() {
- if (!frm.doc.patient) {
- frappe.msgprint({
- title: __('Not Allowed'),
- message: __('Please select Patient first'),
- indicator: 'red'
- });
- } else {
- frappe.call({
- method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
- args: {'patient': frm.doc.patient},
- callback: function(data) {
- if (data.message == true) {
- if (frm.doc.mode_of_payment && frm.doc.paid_amount) {
- check_and_set_availability(frm);
- }
- if (!frm.doc.mode_of_payment) {
- frappe.msgprint({
- title: __('Not Allowed'),
- message: __('Please select a Mode of Payment first'),
- indicator: 'red'
- });
- }
- if (!frm.doc.paid_amount) {
- frappe.msgprint({
- title: __('Not Allowed'),
- message: __('Please set the Paid Amount first'),
- indicator: 'red'
- });
- }
- } else {
- check_and_set_availability(frm);
- }
- }
- });
- }
- });
- } else {
- frm.page.set_primary_action(__('Save'), () => frm.save());
- }
-
- if (frm.doc.patient) {
- frm.add_custom_button(__('Patient History'), function() {
- frappe.route_options = {'patient': frm.doc.patient};
- frappe.set_route('patient_history');
- }, __('View'));
- }
-
- if (frm.doc.status == 'Open' || (frm.doc.status == 'Scheduled' && !frm.doc.__islocal)) {
- frm.add_custom_button(__('Cancel'), function() {
- update_status(frm, 'Cancelled');
- });
- frm.add_custom_button(__('Reschedule'), function() {
- check_and_set_availability(frm);
- });
-
- if (frm.doc.procedure_template) {
- frm.add_custom_button(__('Clinical Procedure'), function(){
- frappe.model.open_mapped_doc({
- method: 'erpnext.healthcare.doctype.clinical_procedure.clinical_procedure.make_procedure',
- frm: frm,
- });
- }, __('Create'));
- } else if (frm.doc.therapy_type) {
- frm.add_custom_button(__('Therapy Session'),function(){
- frappe.model.open_mapped_doc({
- method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.create_therapy_session',
- frm: frm,
- })
- }, 'Create');
- } else {
- frm.add_custom_button(__('Patient Encounter'), function() {
- frappe.model.open_mapped_doc({
- method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.make_encounter',
- frm: frm,
- });
- }, __('Create'));
- }
-
- frm.add_custom_button(__('Vital Signs'), function() {
- create_vital_signs(frm);
- }, __('Create'));
- }
- },
-
- patient: function(frm) {
- if (frm.doc.patient) {
- frm.trigger('toggle_payment_fields');
- frappe.call({
- method: 'frappe.client.get',
- args: {
- doctype: 'Patient',
- name: frm.doc.patient
- },
- callback: function (data) {
- let age = null;
- if (data.message.dob) {
- age = calculate_age(data.message.dob);
- }
- frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age);
- }
- });
- } else {
- frm.set_value('patient_name', '');
- frm.set_value('patient_sex', '');
- frm.set_value('patient_age', '');
- frm.set_value('inpatient_record', '');
- }
- },
-
- practitioner: function(frm) {
- if (frm.doc.practitioner ) {
- frm.events.set_payment_details(frm);
- }
- },
-
- appointment_type: function(frm) {
- if (frm.doc.appointment_type) {
- frm.events.set_payment_details(frm);
- }
- },
-
- set_payment_details: function(frm) {
- frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing').then(val => {
- if (val) {
- frappe.call({
- method: 'erpnext.healthcare.utils.get_service_item_and_practitioner_charge',
- args: {
- doc: frm.doc
- },
- callback: function(data) {
- if (data.message) {
- frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.practitioner_charge);
- frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.service_item);
- }
- }
- });
- }
- });
- },
-
- therapy_plan: function(frm) {
- frm.trigger('set_therapy_type_filter');
- },
-
- set_therapy_type_filter: function(frm) {
- if (frm.doc.therapy_plan) {
- frm.call('get_therapy_types').then(r => {
- frm.set_query('therapy_type', function() {
- return {
- filters: {
- 'name': ['in', r.message]
- }
- };
- });
- });
- }
- },
-
- therapy_type: function(frm) {
- if (frm.doc.therapy_type) {
- frappe.db.get_value('Therapy Type', frm.doc.therapy_type, 'default_duration', (r) => {
- if (r.default_duration) {
- frm.set_value('duration', r.default_duration)
- }
- });
- }
- },
-
- get_procedure_from_encounter: function(frm) {
- get_prescribed_procedure(frm);
- },
-
- toggle_payment_fields: function(frm) {
- frappe.call({
- method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.check_payment_fields_reqd',
- args: {'patient': frm.doc.patient},
- callback: function(data) {
- if (data.message.fee_validity) {
- // if fee validity exists and automated appointment invoicing is enabled,
- // show payment fields as non-mandatory
- frm.toggle_display('mode_of_payment', 0);
- frm.toggle_display('paid_amount', 0);
- frm.toggle_display('billing_item', 0);
- frm.toggle_reqd('mode_of_payment', 0);
- frm.toggle_reqd('paid_amount', 0);
- frm.toggle_reqd('billing_item', 0);
- } else {
- // if automated appointment invoicing is disabled, hide fields
- frm.toggle_display('mode_of_payment', data.message ? 1 : 0);
- frm.toggle_display('paid_amount', data.message ? 1 : 0);
- frm.toggle_display('billing_item', data.message ? 1 : 0);
- frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
- frm.toggle_reqd('paid_amount', data.message ? 1 :0);
- frm.toggle_reqd('billing_item', data.message ? 1 : 0);
- }
- }
- });
- },
-
- get_prescribed_therapies: function(frm) {
- if (frm.doc.patient) {
- frappe.call({
- method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_prescribed_therapies",
- args: {patient: frm.doc.patient},
- callback: function(r) {
- if (r.message) {
- show_therapy_types(frm, r.message);
- } else {
- frappe.msgprint({
- title: __('Not Therapies Prescribed'),
- message: __('There are no Therapies prescribed for Patient {0}', [frm.doc.patient.bold()]),
- indicator: 'blue'
- });
- }
- }
- });
- }
- }
-});
-
-let check_and_set_availability = function(frm) {
- let selected_slot = null;
- let service_unit = null;
- let duration = null;
-
- show_availability();
-
- function show_empty_state(practitioner, appointment_date) {
- frappe.msgprint({
- title: __('Not Available'),
- message: __('Healthcare Practitioner {0} not available on {1}', [practitioner.bold(), appointment_date.bold()]),
- indicator: 'red'
- });
- }
-
- function show_availability() {
- let selected_practitioner = '';
- let d = new frappe.ui.Dialog({
- title: __('Available slots'),
- fields: [
- { fieldtype: 'Link', options: 'Medical Department', reqd: 1, fieldname: 'department', label: 'Medical Department'},
- { fieldtype: 'Column Break'},
- { fieldtype: 'Link', options: 'Healthcare Practitioner', reqd: 1, fieldname: 'practitioner', label: 'Healthcare Practitioner'},
- { fieldtype: 'Column Break'},
- { fieldtype: 'Date', reqd: 1, fieldname: 'appointment_date', label: 'Date'},
- { fieldtype: 'Section Break'},
- { fieldtype: 'HTML', fieldname: 'available_slots'}
-
- ],
- primary_action_label: __('Book'),
- primary_action: function() {
- frm.set_value('appointment_time', selected_slot);
- if (!frm.doc.duration) {
- frm.set_value('duration', duration);
- }
- frm.set_value('practitioner', d.get_value('practitioner'));
- frm.set_value('department', d.get_value('department'));
- frm.set_value('appointment_date', d.get_value('appointment_date'));
- if (service_unit) {
- frm.set_value('service_unit', service_unit);
- }
- d.hide();
- frm.enable_save();
- frm.save();
- d.get_primary_btn().attr('disabled', true);
- }
- });
-
- d.set_values({
- 'department': frm.doc.department,
- 'practitioner': frm.doc.practitioner,
- 'appointment_date': frm.doc.appointment_date
- });
-
- d.fields_dict['department'].df.onchange = () => {
- d.set_values({
- 'practitioner': ''
- });
- let department = d.get_value('department');
- if (department) {
- d.fields_dict.practitioner.get_query = function() {
- return {
- filters: {
- 'department': department
- }
- };
- };
- }
- };
-
- // disable dialog action initially
- d.get_primary_btn().attr('disabled', true);
-
- // Field Change Handler
-
- let fd = d.fields_dict;
-
- d.fields_dict['appointment_date'].df.onchange = () => {
- show_slots(d, fd);
- };
- d.fields_dict['practitioner'].df.onchange = () => {
- if (d.get_value('practitioner') && d.get_value('practitioner') != selected_practitioner) {
- selected_practitioner = d.get_value('practitioner');
- show_slots(d, fd);
- }
- };
- d.show();
- }
-
- function show_slots(d, fd) {
- if (d.get_value('appointment_date') && d.get_value('practitioner')) {
- fd.available_slots.html('');
- frappe.call({
- method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_availability_data',
- args: {
- practitioner: d.get_value('practitioner'),
- date: d.get_value('appointment_date')
- },
- callback: (r) => {
- let data = r.message;
- if (data.slot_details.length > 0) {
- let $wrapper = d.fields_dict.available_slots.$wrapper;
-
- // make buttons for each slot
- let slot_details = data.slot_details;
- let slot_html = '';
- for (let i = 0; i < slot_details.length; i++) {
- slot_html = slot_html + `<label>${slot_details[i].slot_name}</label>`;
- slot_html = slot_html + `<br/>` + slot_details[i].avail_slot.map(slot => {
- let disabled = '';
- let start_str = slot.from_time;
- let slot_start_time = moment(slot.from_time, 'HH:mm:ss');
- let slot_to_time = moment(slot.to_time, 'HH:mm:ss');
- let interval = (slot_to_time - slot_start_time)/60000 | 0;
- // iterate in all booked appointments, update the start time and duration
- slot_details[i].appointments.forEach(function(booked) {
- let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
- let end_time = booked_moment.clone().add(booked.duration, 'minutes');
- // Deal with 0 duration appointments
- if (booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_to_time)) {
- if(booked.duration == 0){
- disabled = 'disabled="disabled"';
- return false;
- }
- }
- // Check for overlaps considering appointment duration
- if (slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)) {
- // There is an overlap
- disabled = 'disabled="disabled"';
- return false;
- }
- });
- return `<button class="btn btn-default"
- data-name=${start_str}
- data-duration=${interval}
- data-service-unit="${slot_details[i].service_unit || ''}"
- style="margin: 0 10px 10px 0; width: 72px;" ${disabled}>
- ${start_str.substring(0, start_str.length - 3)}
- </button>`;
- }).join("");
- slot_html = slot_html + `<br/>`;
- }
-
- $wrapper
- .css('margin-bottom', 0)
- .addClass('text-center')
- .html(slot_html);
-
- // blue button when clicked
- $wrapper.on('click', 'button', function() {
- let $btn = $(this);
- $wrapper.find('button').removeClass('btn-primary');
- $btn.addClass('btn-primary');
- selected_slot = $btn.attr('data-name');
- service_unit = $btn.attr('data-service-unit');
- duration = $btn.attr('data-duration');
- // enable dialog action
- d.get_primary_btn().attr('disabled', null);
- });
-
- } else {
- // fd.available_slots.html('Please select a valid date.'.bold())
- show_empty_state(d.get_value('practitioner'), d.get_value('appointment_date'));
- }
- },
- freeze: true,
- freeze_message: __('Fetching records......')
- });
- } else {
- fd.available_slots.html(__('Appointment date and Healthcare Practitioner are Mandatory').bold());
- }
- }
-};
-
-let get_prescribed_procedure = function(frm) {
- if (frm.doc.patient) {
- frappe.call({
- method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_procedure_prescribed',
- args: {patient: frm.doc.patient},
- callback: function(r) {
- if (r.message && r.message.length) {
- show_procedure_templates(frm, r.message);
- } else {
- frappe.msgprint({
- title: __('Not Found'),
- message: __('No Prescribed Procedures found for the selected Patient')
- });
- }
- }
- });
- } else {
- frappe.msgprint({
- title: __('Not Allowed'),
- message: __('Please select a Patient first')
- });
- }
-};
-
-let show_procedure_templates = function(frm, result){
- let d = new frappe.ui.Dialog({
- title: __('Prescribed Procedures'),
- fields: [
- {
- fieldtype: 'HTML', fieldname: 'procedure_template'
- }
- ]
- });
- let html_field = d.fields_dict.procedure_template.$wrapper;
- html_field.empty();
- $.each(result, function(x, y) {
- let row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
- <div class="col-xs-5"> %(encounter)s <br> %(consulting_practitioner)s <br> %(encounter_date)s </div>\
- <div class="col-xs-5"> %(procedure_template)s <br>%(practitioner)s <br> %(date)s</div>\
- <div class="col-xs-2">\
- <a data-name="%(name)s" data-procedure-template="%(procedure_template)s"\
- data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
- data-date="%(date)s" data-department="%(department)s">\
- <button class="btn btn-default btn-xs">Add\
- </button></a></div></div><div class="col-xs-12"><hr/><div/>', {name:y[0], procedure_template: y[1],
- encounter:y[2], consulting_practitioner:y[3], encounter_date:y[4],
- practitioner:y[5]? y[5]:'', date: y[6]? y[6]:'', department: y[7]? y[7]:''})).appendTo(html_field);
- row.find("a").click(function() {
- frm.doc.procedure_template = $(this).attr('data-procedure-template');
- frm.doc.procedure_prescription = $(this).attr('data-name');
- frm.doc.practitioner = $(this).attr('data-practitioner');
- frm.doc.appointment_date = $(this).attr('data-date');
- frm.doc.department = $(this).attr('data-department');
- refresh_field('procedure_template');
- refresh_field('procedure_prescription');
- refresh_field('appointment_date');
- refresh_field('practitioner');
- refresh_field('department');
- d.hide();
- return false;
- });
- });
- if (!result) {
- let msg = __('There are no procedure prescribed for ') + frm.doc.patient;
- $(repl('<div class="col-xs-12" style="padding-top:20px;" >%(msg)s</div></div>', {msg: msg})).appendTo(html_field);
- }
- d.show();
-};
-
-let show_therapy_types = function(frm, result) {
- var d = new frappe.ui.Dialog({
- title: __('Prescribed Therapies'),
- fields: [
- {
- fieldtype: 'HTML', fieldname: 'therapy_type'
- }
- ]
- });
- var html_field = d.fields_dict.therapy_type.$wrapper;
- $.each(result, function(x, y){
- var row = $(repl('<div class="col-xs-12" style="padding-top:12px; text-align:center;" >\
- <div class="col-xs-5"> %(encounter)s <br> %(practitioner)s <br> %(date)s </div>\
- <div class="col-xs-5"> %(therapy)s </div>\
- <div class="col-xs-2">\
- <a data-therapy="%(therapy)s" data-therapy-plan="%(therapy_plan)s" data-name="%(name)s"\
- data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
- data-date="%(date)s" data-department="%(department)s">\
- <button class="btn btn-default btn-xs">Add\
- </button></a></div></div><div class="col-xs-12"><hr/><div/>', {therapy:y[0],
- name: y[1], encounter:y[2], practitioner:y[3], date:y[4],
- department:y[6]? y[6]:'', therapy_plan:y[5]})).appendTo(html_field);
-
- row.find("a").click(function() {
- frm.doc.therapy_type = $(this).attr("data-therapy");
- frm.doc.practitioner = $(this).attr("data-practitioner");
- frm.doc.department = $(this).attr("data-department");
- frm.doc.therapy_plan = $(this).attr("data-therapy-plan");
- frm.refresh_field("therapy_type");
- frm.refresh_field("practitioner");
- frm.refresh_field("department");
- frm.refresh_field("therapy-plan");
- frappe.db.get_value('Therapy Type', frm.doc.therapy_type, 'default_duration', (r) => {
- if (r.default_duration) {
- frm.set_value('duration', r.default_duration)
- }
- });
- d.hide();
- return false;
- });
- });
- d.show();
-};
-
-let create_vital_signs = function(frm) {
- if (!frm.doc.patient) {
- frappe.throw(__('Please select patient'));
- }
- frappe.route_options = {
- 'patient': frm.doc.patient,
- 'appointment': frm.doc.name,
- 'company': frm.doc.company
- };
- frappe.new_doc('Vital Signs');
-};
-
-let update_status = function(frm, status){
- let doc = frm.doc;
- frappe.confirm(__('Are you sure you want to cancel this appointment?'),
- function() {
- frappe.call({
- method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status',
- args: {appointment_id: doc.name, status:status},
- callback: function(data) {
- if (!data.exc) {
- frm.reload_doc();
- }
- }
- });
- }
- );
-};
-
-let calculate_age = function(birth) {
- let ageMS = Date.parse(Date()) - Date.parse(birth);
- let age = new Date();
- age.setTime(ageMS);
- let years = age.getFullYear() - 1970;
- return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
-};
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
deleted file mode 100644
index 83c92af..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ /dev/null
@@ -1,403 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2017-05-04 11:52:40.941507",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "title",
- "status",
- "patient",
- "patient_name",
- "patient_sex",
- "patient_age",
- "inpatient_record",
- "column_break_1",
- "company",
- "practitioner",
- "practitioner_name",
- "department",
- "service_unit",
- "section_break_12",
- "appointment_type",
- "duration",
- "procedure_template",
- "get_procedure_from_encounter",
- "procedure_prescription",
- "therapy_plan",
- "therapy_type",
- "get_prescribed_therapies",
- "column_break_17",
- "appointment_date",
- "appointment_time",
- "appointment_datetime",
- "section_break_16",
- "mode_of_payment",
- "billing_item",
- "invoiced",
- "column_break_2",
- "paid_amount",
- "ref_sales_invoice",
- "section_break_3",
- "referring_practitioner",
- "reminded",
- "column_break_36",
- "notes"
- ],
- "fields": [
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1,
- "search_index": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "appointment_type",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Appointment Type",
- "options": "Appointment Type",
- "set_only_once": 1
- },
- {
- "fetch_from": "appointment_type.default_duration",
- "fieldname": "duration",
- "fieldtype": "Int",
- "in_filter": 1,
- "label": "Duration (In Minutes)",
- "set_only_once": 1
- },
- {
- "fieldname": "column_break_1",
- "fieldtype": "Column Break",
- "read_only": 1
- },
- {
- "depends_on": "eval:!doc.__islocal",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Status",
- "options": "\nScheduled\nOpen\nClosed\nCancelled",
- "read_only": 1,
- "search_index": 1
- },
- {
- "depends_on": "eval:doc.patient;",
- "fieldname": "procedure_template",
- "fieldtype": "Link",
- "label": "Clinical Procedure Template",
- "options": "Clinical Procedure Template",
- "set_only_once": 1
- },
- {
- "depends_on": "eval:doc.__islocal && doc.patient",
- "fieldname": "get_procedure_from_encounter",
- "fieldtype": "Button",
- "label": "Get Prescribed Clinical Procedures"
- },
- {
- "fieldname": "procedure_prescription",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Procedure Prescription",
- "no_copy": 1,
- "options": "Procedure Prescription",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "service_unit",
- "fieldtype": "Link",
- "label": "Service Unit",
- "options": "Healthcare Service Unit",
- "set_only_once": 1
- },
- {
- "fieldname": "section_break_12",
- "fieldtype": "Section Break",
- "label": "Appointment Details"
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "reqd": 1,
- "search_index": 1,
- "set_only_once": 1
- },
- {
- "fetch_from": "practitioner.department",
- "fieldname": "department",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Department",
- "options": "Medical Department",
- "search_index": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "column_break_17",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "appointment_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Date",
- "read_only": 1,
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "appointment_time",
- "fieldtype": "Time",
- "in_list_view": 1,
- "label": "Time",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "section_break_16",
- "fieldtype": "Section Break",
- "label": "Payments"
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fetch_from": "patient.sex",
- "fieldname": "patient_sex",
- "fieldtype": "Link",
- "label": "Gender",
- "no_copy": 1,
- "options": "Gender",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "label": "Patient Age",
- "read_only": 1
- },
- {
- "fieldname": "appointment_datetime",
- "fieldtype": "Datetime",
- "hidden": 1,
- "label": "Appointment Datetime",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1,
- "search_index": 1
- },
- {
- "fieldname": "mode_of_payment",
- "fieldtype": "Link",
- "label": "Mode of Payment",
- "options": "Mode of Payment",
- "read_only_depends_on": "invoiced"
- },
- {
- "fieldname": "paid_amount",
- "fieldtype": "Currency",
- "label": "Paid Amount",
- "read_only_depends_on": "invoiced"
- },
- {
- "fieldname": "column_break_2",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "read_only": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Company",
- "no_copy": 1,
- "options": "Company",
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_3",
- "fieldtype": "Section Break",
- "label": "More Info"
- },
- {
- "fieldname": "notes",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Notes"
- },
- {
- "fieldname": "referring_practitioner",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Referring Practitioner",
- "options": "Healthcare Practitioner"
- },
- {
- "default": "0",
- "fieldname": "reminded",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Reminded",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "depends_on": "eval:doc.patient && doc.therapy_plan;",
- "fieldname": "therapy_type",
- "fieldtype": "Link",
- "label": "Therapy",
- "options": "Therapy Type",
- "set_only_once": 1
- },
- {
- "depends_on": "eval:doc.patient && doc.therapy_plan && doc.__islocal;",
- "fieldname": "get_prescribed_therapies",
- "fieldtype": "Button",
- "label": "Get Prescribed Therapies"
- },
- {
- "depends_on": "eval: doc.patient;",
- "fieldname": "therapy_plan",
- "fieldtype": "Link",
- "label": "Therapy Plan",
- "options": "Therapy Plan",
- "set_only_once": 1
- },
- {
- "fieldname": "ref_sales_invoice",
- "fieldtype": "Link",
- "label": "Reference Sales Invoice",
- "options": "Sales Invoice",
- "read_only": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-APP-.YYYY.-",
- "set_only_once": 1
- },
- {
- "fieldname": "billing_item",
- "fieldtype": "Link",
- "label": "Billing Item",
- "options": "Item",
- "read_only": 1
- },
- {
- "fieldname": "column_break_36",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fetch_from": "practitioner.practitioner_name",
- "fieldname": "practitioner_name",
- "fieldtype": "Data",
- "label": "Practitioner Name",
- "read_only": 1
- }
- ],
- "links": [],
- "modified": "2021-02-08 13:13:15.116833",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Appointment",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient, practitioner, department, appointment_date, appointment_time",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
deleted file mode 100755
index cdd4ad3..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ /dev/null
@@ -1,509 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-import json
-from frappe.utils import getdate, get_time, flt
-from frappe.model.mapper import get_mapped_doc
-from frappe import _
-import datetime
-from frappe.core.doctype.sms_settings.sms_settings import send_sms
-from erpnext.hr.doctype.employee.employee import is_holiday
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
-from erpnext.healthcare.utils import check_fee_validity, get_service_item_and_practitioner_charge, manage_fee_validity
-
-class PatientAppointment(Document):
- def validate(self):
- self.validate_overlaps()
- self.validate_service_unit()
- self.set_appointment_datetime()
- self.validate_customer_created()
- self.set_status()
- self.set_title()
-
- def after_insert(self):
- self.update_prescription_details()
- self.set_payment_details()
- invoice_appointment(self)
- self.update_fee_validity()
- send_confirmation_msg(self)
-
- def set_title(self):
- self.title = _('{0} with {1}').format(self.patient_name or self.patient,
- self.practitioner_name or self.practitioner)
-
- def set_status(self):
- today = getdate()
- appointment_date = getdate(self.appointment_date)
-
- # If appointment is created for today set status as Open else Scheduled
- if appointment_date == today:
- self.status = 'Open'
- elif appointment_date > today:
- self.status = 'Scheduled'
-
- def validate_overlaps(self):
- end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \
- + datetime.timedelta(minutes=flt(self.duration))
-
- overlaps = frappe.db.sql("""
- select
- name, practitioner, patient, appointment_time, duration
- from
- `tabPatient Appointment`
- where
- appointment_date=%s and name!=%s and status NOT IN ("Closed", "Cancelled")
- and (practitioner=%s or patient=%s) and
- ((appointment_time<%s and appointment_time + INTERVAL duration MINUTE>%s) or
- (appointment_time>%s and appointment_time<%s) or
- (appointment_time=%s))
- """, (self.appointment_date, self.name, self.practitioner, self.patient,
- self.appointment_time, end_time.time(), self.appointment_time, end_time.time(), self.appointment_time))
-
- if overlaps:
- overlapping_details = _('Appointment overlaps with ')
- overlapping_details += "<b><a href='/app/Form/Patient Appointment/{0}'>{0}</a></b><br>".format(overlaps[0][0])
- overlapping_details += _('{0} has appointment scheduled with {1} at {2} having {3} minute(s) duration.').format(
- overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])
- frappe.throw(overlapping_details, title=_('Appointments Overlapping'))
-
- def validate_service_unit(self):
- if self.inpatient_record and self.service_unit:
- from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
-
- is_inpatient_occupancy_unit = frappe.db.get_value('Healthcare Service Unit', self.service_unit,
- 'inpatient_occupancy')
- service_unit = get_current_healthcare_service_unit(self.inpatient_record)
- if is_inpatient_occupancy_unit and service_unit != self.service_unit:
- msg = _('Patient {0} is not admitted in the service unit {1}').format(frappe.bold(self.patient), frappe.bold(self.service_unit)) + '<br>'
- msg += _('Appointment for service units with Inpatient Occupancy can only be created against the unit where patient has been admitted.')
- frappe.throw(msg, title=_('Invalid Healthcare Service Unit'))
-
-
- def set_appointment_datetime(self):
- self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
-
- def set_payment_details(self):
- if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
- details = get_service_item_and_practitioner_charge(self)
- self.db_set('billing_item', details.get('service_item'))
- if not self.paid_amount:
- self.db_set('paid_amount', details.get('practitioner_charge'))
-
- def validate_customer_created(self):
- if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
- if not frappe.db.get_value('Patient', self.patient, 'customer'):
- msg = _("Please set a Customer linked to the Patient")
- msg += " <b><a href='/app/Form/Patient/{0}'>{0}</a></b>".format(self.patient)
- frappe.throw(msg, title=_('Customer Not Found'))
-
- def update_prescription_details(self):
- if self.procedure_prescription:
- frappe.db.set_value('Procedure Prescription', self.procedure_prescription, 'appointment_booked', 1)
- if self.procedure_template:
- comments = frappe.db.get_value('Procedure Prescription', self.procedure_prescription, 'comments')
- if comments:
- frappe.db.set_value('Patient Appointment', self.name, 'notes', comments)
-
- def update_fee_validity(self):
- fee_validity = manage_fee_validity(self)
- if fee_validity:
- frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
-
- @frappe.whitelist()
- def get_therapy_types(self):
- if not self.therapy_plan:
- return
-
- therapy_types = []
- doc = frappe.get_doc('Therapy Plan', self.therapy_plan)
- for entry in doc.therapy_plan_details:
- therapy_types.append(entry.therapy_type)
-
- return therapy_types
-
-
-@frappe.whitelist()
-def check_payment_fields_reqd(patient):
- automate_invoicing = frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing')
- free_follow_ups = frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups')
- if automate_invoicing:
- if free_follow_ups:
- fee_validity = frappe.db.exists('Fee Validity', {'patient': patient, 'status': 'Pending'})
- if fee_validity:
- return {'fee_validity': fee_validity}
- if check_is_new_patient(patient):
- return False
- return True
- return False
-
-def invoice_appointment(appointment_doc):
- automate_invoicing = frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing')
- appointment_invoiced = frappe.db.get_value('Patient Appointment', appointment_doc.name, 'invoiced')
- enable_free_follow_ups = frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups')
- if enable_free_follow_ups:
- fee_validity = check_fee_validity(appointment_doc)
- if fee_validity and fee_validity.status == 'Completed':
- fee_validity = None
- elif not fee_validity:
- if frappe.db.exists('Fee Validity Reference', {'appointment': appointment_doc.name}):
- return
- if check_is_new_patient(appointment_doc.patient, appointment_doc.name):
- return
- else:
- fee_validity = None
-
- if automate_invoicing and not appointment_invoiced and not fee_validity:
- create_sales_invoice(appointment_doc)
-
-
-def create_sales_invoice(appointment_doc):
- sales_invoice = frappe.new_doc('Sales Invoice')
- sales_invoice.patient = appointment_doc.patient
- sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
- sales_invoice.appointment = appointment_doc.name
- sales_invoice.due_date = getdate()
- sales_invoice.company = appointment_doc.company
- sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
-
- item = sales_invoice.append('items', {})
- item = get_appointment_item(appointment_doc, item)
-
- # Add payments if payment details are supplied else proceed to create invoice as Unpaid
- if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
- sales_invoice.is_pos = 1
- payment = sales_invoice.append('payments', {})
- payment.mode_of_payment = appointment_doc.mode_of_payment
- payment.amount = appointment_doc.paid_amount
-
- sales_invoice.set_missing_values(for_validate=True)
- sales_invoice.flags.ignore_mandatory = True
- sales_invoice.save(ignore_permissions=True)
- sales_invoice.submit()
- frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True)
- frappe.db.set_value('Patient Appointment', appointment_doc.name, {
- 'invoiced': 1,
- 'ref_sales_invoice': sales_invoice.name
- })
-
-
-def check_is_new_patient(patient, name=None):
- filters = {'patient': patient, 'status': ('!=','Cancelled')}
- if name:
- filters['name'] = ('!=', name)
-
- has_previous_appointment = frappe.db.exists('Patient Appointment', filters)
- if has_previous_appointment:
- return False
- return True
-
-
-def get_appointment_item(appointment_doc, item):
- details = get_service_item_and_practitioner_charge(appointment_doc)
- charge = appointment_doc.paid_amount or details.get('practitioner_charge')
- item.item_code = details.get('service_item')
- item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner)
- item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company)
- item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center')
- item.rate = charge
- item.amount = charge
- item.qty = 1
- item.reference_dt = 'Patient Appointment'
- item.reference_dn = appointment_doc.name
- return item
-
-
-def cancel_appointment(appointment_id):
- appointment = frappe.get_doc('Patient Appointment', appointment_id)
- if appointment.invoiced:
- sales_invoice = check_sales_invoice_exists(appointment)
- if sales_invoice and cancel_sales_invoice(sales_invoice):
- msg = _('Appointment {0} and Sales Invoice {1} cancelled').format(appointment.name, sales_invoice.name)
- else:
- msg = _('Appointment Cancelled. Please review and cancel the invoice {0}').format(sales_invoice.name)
- else:
- fee_validity = manage_fee_validity(appointment)
- msg = _('Appointment Cancelled.')
- if fee_validity:
- msg += _('Fee Validity {0} updated.').format(fee_validity.name)
-
- frappe.msgprint(msg)
-
-
-def cancel_sales_invoice(sales_invoice):
- if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
- if len(sales_invoice.items) == 1:
- sales_invoice.cancel()
- return True
- return False
-
-
-def check_sales_invoice_exists(appointment):
- sales_invoice = frappe.db.get_value('Sales Invoice Item', {
- 'reference_dt': 'Patient Appointment',
- 'reference_dn': appointment.name
- }, 'parent')
-
- if sales_invoice:
- sales_invoice = frappe.get_doc('Sales Invoice', sales_invoice)
- return sales_invoice
- return False
-
-
-@frappe.whitelist()
-def get_availability_data(date, practitioner):
- """
- Get availability data of 'practitioner' on 'date'
- :param date: Date to check in schedule
- :param practitioner: Name of the practitioner
- :return: dict containing a list of available slots, list of appointments and time of appointments
- """
-
- date = getdate(date)
- weekday = date.strftime('%A')
-
- practitioner_doc = frappe.get_doc('Healthcare Practitioner', practitioner)
-
- check_employee_wise_availability(date, practitioner_doc)
-
- if practitioner_doc.practitioner_schedules:
- slot_details = get_available_slots(practitioner_doc, date)
- else:
- frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master').format(
- practitioner), title=_('Practitioner Schedule Not Found'))
-
-
- if not slot_details:
- # TODO: return available slots in nearby dates
- frappe.throw(_('Healthcare Practitioner not available on {0}').format(weekday), title=_('Not Available'))
-
- return {'slot_details': slot_details}
-
-
-def check_employee_wise_availability(date, practitioner_doc):
- employee = None
- if practitioner_doc.employee:
- employee = practitioner_doc.employee
- elif practitioner_doc.user_id:
- employee = frappe.db.get_value('Employee', {'user_id': practitioner_doc.user_id}, 'name')
-
- if employee:
- # check holiday
- if is_holiday(employee, date):
- frappe.throw(_('{0} is a holiday'.format(date)), title=_('Not Available'))
-
- # check leave status
- 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""", (employee, date), as_dict=True)
- if leave_record:
- if leave_record[0].half_day:
- frappe.throw(_('{0} is on a Half day Leave on {1}').format(practitioner_doc.name, date), title=_('Not Available'))
- else:
- frappe.throw(_('{0} is on Leave on {1}').format(practitioner_doc.name, date), title=_('Not Available'))
-
-
-def get_available_slots(practitioner_doc, date):
- available_slots = []
- slot_details = []
- weekday = date.strftime('%A')
- practitioner = practitioner_doc.name
-
- for schedule_entry in practitioner_doc.practitioner_schedules:
- if schedule_entry.schedule:
- practitioner_schedule = frappe.get_doc('Practitioner Schedule', schedule_entry.schedule)
- else:
- frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner').format(
- frappe.bold(practitioner)), title=_('Practitioner Schedule Not Found'))
-
- if practitioner_schedule:
- available_slots = []
- for time_slot in practitioner_schedule.time_slots:
- if weekday == time_slot.day:
- available_slots.append(time_slot)
-
- if available_slots:
- appointments = []
- # fetch all appointments to practitioner by service unit
- filters = {
- 'practitioner': practitioner,
- 'service_unit': schedule_entry.service_unit,
- 'appointment_date': date,
- 'status': ['not in',['Cancelled']]
- }
-
- if schedule_entry.service_unit:
- slot_name = schedule_entry.schedule + ' - ' + schedule_entry.service_unit
- allow_overlap = frappe.get_value('Healthcare Service Unit', schedule_entry.service_unit, 'overlap_appointments')
- if not allow_overlap:
- # fetch all appointments to service unit
- filters.pop('practitioner')
- else:
- slot_name = schedule_entry.schedule
- # fetch all appointments to practitioner without service unit
- filters['practitioner'] = practitioner
- filters.pop('service_unit')
-
- appointments = frappe.get_all(
- 'Patient Appointment',
- filters=filters,
- fields=['name', 'appointment_time', 'duration', 'status'])
-
- slot_details.append({'slot_name':slot_name, 'service_unit':schedule_entry.service_unit,
- 'avail_slot':available_slots, 'appointments': appointments})
-
- return slot_details
-
-
-@frappe.whitelist()
-def update_status(appointment_id, status):
- frappe.db.set_value('Patient Appointment', appointment_id, 'status', status)
- appointment_booked = True
- if status == 'Cancelled':
- appointment_booked = False
- cancel_appointment(appointment_id)
-
- procedure_prescription = frappe.db.get_value('Patient Appointment', appointment_id, 'procedure_prescription')
- if procedure_prescription:
- frappe.db.set_value('Procedure Prescription', procedure_prescription, 'appointment_booked', appointment_booked)
-
-
-def send_confirmation_msg(doc):
- if frappe.db.get_single_value('Healthcare Settings', 'send_appointment_confirmation'):
- message = frappe.db.get_single_value('Healthcare Settings', 'appointment_confirmation_msg')
- try:
- send_message(doc, message)
- except Exception:
- frappe.log_error(frappe.get_traceback(), _('Appointment Confirmation Message Not Sent'))
- frappe.msgprint(_('Appointment Confirmation Message Not Sent'), indicator='orange')
-
-
-@frappe.whitelist()
-def make_encounter(source_name, target_doc=None):
- doc = get_mapped_doc('Patient Appointment', source_name, {
- 'Patient Appointment': {
- 'doctype': 'Patient Encounter',
- 'field_map': [
- ['appointment', 'name'],
- ['patient', 'patient'],
- ['practitioner', 'practitioner'],
- ['medical_department', 'department'],
- ['patient_sex', 'patient_sex'],
- ['invoiced', 'invoiced'],
- ['company', 'company']
- ]
- }
- }, target_doc)
- return doc
-
-
-def send_appointment_reminder():
- if frappe.db.get_single_value('Healthcare Settings', 'send_appointment_reminder'):
- remind_before = datetime.datetime.strptime(frappe.db.get_single_value('Healthcare Settings', 'remind_before'), '%H:%M:%S')
- reminder_dt = datetime.datetime.now() + datetime.timedelta(
- hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second)
-
- appointment_list = frappe.db.get_all('Patient Appointment', {
- 'appointment_datetime': ['between', (datetime.datetime.now(), reminder_dt)],
- 'reminded': 0,
- 'status': ['!=', 'Cancelled']
- })
-
- for appointment in appointment_list:
- doc = frappe.get_doc('Patient Appointment', appointment.name)
- message = frappe.db.get_single_value('Healthcare Settings', 'appointment_reminder_msg')
- send_message(doc, message)
- frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1)
-
-def send_message(doc, message):
- patient_mobile = frappe.db.get_value('Patient', doc.patient, 'mobile')
- if patient_mobile:
- context = {'doc': doc, 'alert': doc, 'comments': None}
- if doc.get('_comments'):
- context['comments'] = json.loads(doc.get('_comments'))
-
- # jinja to string convertion happens here
- message = frappe.render_template(message, context)
- number = [patient_mobile]
- try:
- send_sms(number, message)
- except Exception as e:
- frappe.msgprint(_('SMS not sent, please check SMS Settings'), alert=True)
-
-@frappe.whitelist()
-def get_events(start, end, filters=None):
- """Returns events for Gantt / 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('Patient Appointment', filters)
-
- data = frappe.db.sql("""
- select
- `tabPatient Appointment`.name, `tabPatient Appointment`.patient,
- `tabPatient Appointment`.practitioner, `tabPatient Appointment`.status,
- `tabPatient Appointment`.duration,
- timestamp(`tabPatient Appointment`.appointment_date, `tabPatient Appointment`.appointment_time) as 'start',
- `tabAppointment Type`.color
- from
- `tabPatient Appointment`
- left join `tabAppointment Type` on `tabPatient Appointment`.appointment_type=`tabAppointment Type`.name
- where
- (`tabPatient Appointment`.appointment_date between %(start)s and %(end)s)
- and `tabPatient Appointment`.status != 'Cancelled' and `tabPatient Appointment`.docstatus < 2 {conditions}""".format(conditions=conditions),
- {"start": start, "end": end}, as_dict=True, update={"allDay": 0})
-
- for item in data:
- item.end = item.start + datetime.timedelta(minutes = item.duration)
-
- return data
-
-
-@frappe.whitelist()
-def get_procedure_prescribed(patient):
- return frappe.db.sql(
- """
- SELECT
- pp.name, pp.procedure, pp.parent, ct.practitioner,
- ct.encounter_date, pp.practitioner, pp.date, pp.department
- FROM
- `tabPatient Encounter` ct, `tabProcedure Prescription` pp
- WHERE
- ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0
- ORDER BY
- ct.creation desc
- """, {'patient': patient}
- )
-
-
-@frappe.whitelist()
-def get_prescribed_therapies(patient):
- return frappe.db.sql(
- """
- SELECT
- t.therapy_type, t.name, t.parent, e.practitioner,
- e.encounter_date, e.therapy_plan, e.medical_department
- FROM
- `tabPatient Encounter` e, `tabTherapy Plan Detail` t
- WHERE
- e.patient=%(patient)s and t.parent=e.name
- ORDER BY
- e.creation desc
- """, {'patient': patient}
- )
-
-
-def update_appointment_status():
- # update the status of appointments daily
- appointments = frappe.get_all('Patient Appointment', {
- 'status': ('not in', ['Closed', 'Cancelled'])
- }, as_dict=1)
-
- for appointment in appointments:
- frappe.get_doc('Patient Appointment', appointment.name).set_status()
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js
deleted file mode 100644
index 2249d2a..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_calendar.js
+++ /dev/null
@@ -1,14 +0,0 @@
-
-frappe.views.calendar["Patient Appointment"] = {
- field_map: {
- "start": "start",
- "end": "end",
- "id": "name",
- "title": "patient",
- "allDay": "allDay",
- "eventColor": "color"
- },
- order_by: "appointment_date",
- gantt: true,
- get_events_method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_events"
-};
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py
deleted file mode 100644
index 085c4f6..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_dashboard.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'appointment',
- 'non_standard_fieldnames': {
- 'Patient Medical Record': 'reference_name'
- },
- 'transactions': [
- {
- 'label': _('Consultations'),
- 'items': ['Patient Encounter', 'Vital Signs', 'Patient Medical Record']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js
deleted file mode 100644
index 721887b..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment_list.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
-(c) ESS 2015-16
-*/
-frappe.listview_settings['Patient Appointment'] = {
- filters: [["status", "=", "Open"]],
- get_indicator: function(doc) {
- var colors = {
- "Open": "orange",
- "Scheduled": "yellow",
- "Closed": "green",
- "Cancelled": "red",
- "Expired": "grey"
- };
- return [__(doc.status), colors[doc.status], "status,=," + doc.status];
- }
-};
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js
deleted file mode 100644
index 71fc177..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient Appointment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Patient Appointment
- () => frappe.tests.make('Patient Appointment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
deleted file mode 100644
index 5f2dc48..0000000
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ /dev/null
@@ -1,298 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-import frappe
-from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter
-from frappe.utils import nowdate, add_days, now_datetime
-from frappe.utils.make_random import get_random
-from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
-
-class TestPatientAppointment(unittest.TestCase):
- def setUp(self):
- frappe.db.sql("""delete from `tabPatient Appointment`""")
- frappe.db.sql("""delete from `tabFee Validity`""")
- frappe.db.sql("""delete from `tabPatient Encounter`""")
- make_pos_profile()
-
- def test_status(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
- appointment = create_appointment(patient, practitioner, nowdate())
- self.assertEqual(appointment.status, 'Open')
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
- self.assertEqual(appointment.status, 'Scheduled')
- encounter = create_encounter(appointment)
- self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
- encounter.cancel()
- self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
-
- def test_start_encounter(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
- appointment.reload()
- self.assertEqual(appointment.invoiced, 1)
- encounter = make_encounter(appointment.name)
- self.assertTrue(encounter)
- self.assertEqual(encounter.company, appointment.company)
- self.assertEqual(encounter.practitioner, appointment.practitioner)
- self.assertEqual(encounter.patient, appointment.patient)
- # invoiced flag mapped from appointment
- self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
-
- def test_auto_invoicing(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
- appointment = create_appointment(patient, practitioner, nowdate())
- self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 0)
-
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1)
- self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
- sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
- self.assertTrue(sales_invoice_name)
- self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'company'), appointment.company)
- self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient)
- self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
-
- def test_auto_invoicing_based_on_department(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- appointment_type = create_appointment_type()
-
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
- invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department')
- appointment.reload()
-
- self.assertEqual(appointment.invoiced, 1)
- self.assertEqual(appointment.billing_item, 'HLC-SI-001')
- self.assertEqual(appointment.paid_amount, 200)
-
- sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
- self.assertTrue(sales_invoice_name)
- self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
-
- def test_auto_invoicing_according_to_appointment_type_charge(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
-
- item = create_healthcare_service_items()
- items = [{
- 'op_consulting_charge_item': item,
- 'op_consulting_charge': 300
- }]
- appointment_type = create_appointment_type(args={
- 'name': 'Generic Appointment Type charge',
- 'items': items
- })
-
- appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
- invoice=1, appointment_type=appointment_type.name)
- appointment.reload()
-
- self.assertEqual(appointment.invoiced, 1)
- self.assertEqual(appointment.billing_item, item)
- self.assertEqual(appointment.paid_amount, 300)
-
- sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
- self.assertTrue(sales_invoice_name)
-
- def test_appointment_cancel(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
- appointment = create_appointment(patient, practitioner, nowdate())
- fee_validity = frappe.db.get_value('Fee Validity Reference', {'appointment': appointment.name}, 'parent')
- # fee validity created
- self.assertTrue(fee_validity)
-
- visited = frappe.db.get_value('Fee Validity', fee_validity, 'visited')
- update_status(appointment.name, 'Cancelled')
- # check fee validity updated
- self.assertEqual(frappe.db.get_value('Fee Validity', fee_validity, 'visited'), visited - 1)
-
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
- update_status(appointment.name, 'Cancelled')
- # check invoice cancelled
- sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
- self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'status'), 'Cancelled')
-
- def test_appointment_booking_for_admission_service_unit(self):
- from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
- from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import \
- create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
-
- frappe.db.sql("""delete from `tabInpatient Record`""")
- patient, medical_department, practitioner = create_healthcare_docs()
- patient = create_patient()
- # Schedule Admission
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save(ignore_permissions = True)
-
- # Admit
- service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
- admit_patient(ip_record, service_unit, now_datetime())
-
- appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit)
- self.assertEqual(appointment.service_unit, service_unit)
-
- # Discharge
- schedule_discharge(frappe.as_json({'patient': patient}))
- ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
- mark_invoiced_inpatient_occupancy(ip_record1)
- discharge_patient(ip_record1)
-
- def test_invalid_healthcare_service_unit_validation(self):
- from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
- from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import \
- create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
-
- frappe.db.sql("""delete from `tabInpatient Record`""")
- patient, medical_department, practitioner = create_healthcare_docs()
- patient = create_patient()
- # Schedule Admission
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save(ignore_permissions = True)
-
- # Admit
- service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
- admit_patient(ip_record, service_unit, now_datetime())
-
- appointment_service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy for Appointment')
- appointment = create_appointment(patient, practitioner, nowdate(), service_unit=appointment_service_unit, save=0)
- self.assertRaises(frappe.exceptions.ValidationError, appointment.save)
-
- # Discharge
- schedule_discharge(frappe.as_json({'patient': patient}))
- ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
- mark_invoiced_inpatient_occupancy(ip_record1)
- discharge_patient(ip_record1)
-
-
-def create_healthcare_docs():
- patient = create_patient()
- practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
- medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
-
- if not medical_department:
- medical_department = frappe.new_doc('Medical Department')
- medical_department.department = '_Test Medical Department'
- medical_department.save(ignore_permissions=True)
- medical_department = medical_department.name
-
- if not practitioner:
- practitioner = frappe.new_doc('Healthcare Practitioner')
- practitioner.first_name = '_Test Healthcare Practitioner'
- practitioner.gender = 'Female'
- practitioner.department = medical_department
- practitioner.op_consulting_charge = 500
- practitioner.inpatient_visit_charge = 500
- practitioner.save(ignore_permissions=True)
- practitioner = practitioner.name
-
- return patient, medical_department, practitioner
-
-def create_patient():
- patient = frappe.db.exists('Patient', '_Test Patient')
- if not patient:
- patient = frappe.new_doc('Patient')
- patient.first_name = '_Test Patient'
- patient.sex = 'Female'
- patient.save(ignore_permissions=True)
- patient = patient.name
- return patient
-
-def create_encounter(appointment):
- if appointment:
- encounter = frappe.new_doc('Patient Encounter')
- encounter.appointment = appointment.name
- encounter.patient = appointment.patient
- encounter.practitioner = appointment.practitioner
- encounter.encounter_date = appointment.appointment_date
- encounter.encounter_time = appointment.appointment_time
- encounter.company = appointment.company
- encounter.save()
- encounter.submit()
- return encounter
-
-def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0,
- service_unit=None, appointment_type=None, save=1, department=None):
- item = create_healthcare_service_items()
- frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item)
- frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item)
- appointment = frappe.new_doc('Patient Appointment')
- appointment.patient = patient
- appointment.practitioner = practitioner
- appointment.department = department or '_Test Medical Department'
- appointment.appointment_date = appointment_date
- appointment.company = '_Test Company'
- appointment.duration = 15
- if service_unit:
- appointment.service_unit = service_unit
- if invoice:
- appointment.mode_of_payment = 'Cash'
- if appointment_type:
- appointment.appointment_type = appointment_type
- if procedure_template:
- appointment.procedure_template = create_clinical_procedure_template().get('name')
- if save:
- appointment.save(ignore_permissions=True)
- return appointment
-
-def create_healthcare_service_items():
- if frappe.db.exists('Item', 'HLC-SI-001'):
- return 'HLC-SI-001'
- item = frappe.new_doc('Item')
- item.item_code = 'HLC-SI-001'
- item.item_name = 'Consulting Charges'
- item.item_group = 'Services'
- item.is_stock_item = 0
- item.stock_uom = 'Nos'
- item.save()
- return item.name
-
-def create_clinical_procedure_template():
- if frappe.db.exists('Clinical Procedure Template', 'Knee Surgery and Rehab'):
- return frappe.get_doc('Clinical Procedure Template', 'Knee Surgery and Rehab')
- template = frappe.new_doc('Clinical Procedure Template')
- template.template = 'Knee Surgery and Rehab'
- template.item_code = 'Knee Surgery and Rehab'
- template.item_group = 'Services'
- template.is_billable = 1
- template.description = 'Knee Surgery and Rehab'
- template.rate = 50000
- template.save()
- return template
-
-def create_appointment_type(args=None):
- if not args:
- args = frappe.local.form_dict
-
- name = args.get('name') or 'Test Appointment Type wise Charge'
-
- if frappe.db.exists('Appointment Type', name):
- return frappe.get_doc('Appointment Type', name)
-
- else:
- item = create_healthcare_service_items()
- items = [{
- 'medical_department': '_Test Medical Department',
- 'op_consulting_charge_item': item,
- 'op_consulting_charge': 200
- }]
- return frappe.get_doc({
- 'doctype': 'Appointment Type',
- 'appointment_type': args.get('name') or 'Test Appointment Type wise Charge',
- 'default_duration': args.get('default_duration') or 20,
- 'color': args.get('color') or '#7575ff',
- 'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}),
- 'items': args.get('items') or items
- }).insert()
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js
deleted file mode 100644
index f28d32c..0000000
--- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient Assessment', {
- refresh: function(frm) {
- if (frm.doc.assessment_template) {
- frm.trigger('set_score_range');
- }
-
- if (!frm.doc.__islocal) {
- frm.trigger('show_patient_progress');
- }
- },
-
- assessment_template: function(frm) {
- if (frm.doc.assessment_template) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Patient Assessment Template',
- name: frm.doc.assessment_template
- },
- callback: function(data) {
- frm.doc.assessment_sheet = [];
- $.each(data.message.parameters, function(_i, e) {
- let entry = frm.add_child('assessment_sheet');
- entry.parameter = e.assessment_parameter;
- });
-
- frm.set_value('scale_min', data.message.scale_min);
- frm.set_value('scale_max', data.message.scale_max);
- frm.set_value('assessment_description', data.message.assessment_description);
- frm.set_value('total_score', data.message.scale_max * data.message.parameters.length);
- frm.trigger('set_score_range');
- refresh_field('assessment_sheet');
- }
- });
- }
- },
-
- set_score_range: function(frm) {
- let options = [''];
- for(let i = frm.doc.scale_min; i <= frm.doc.scale_max; i++) {
- options.push(i);
- }
- frm.fields_dict.assessment_sheet.grid.update_docfield_property(
- 'score', 'options', options
- );
- },
-
- calculate_total_score: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- let total_score = 0;
- $.each(frm.doc.assessment_sheet || [], function(_i, item) {
- if (item.score) {
- total_score += parseInt(item.score);
- }
- });
-
- frm.set_value('total_score_obtained', total_score);
- },
-
- show_patient_progress: function(frm) {
- let bars = [];
- let message = '';
- let added_min = false;
-
- let title = __('{0} out of {1}', [frm.doc.total_score_obtained, frm.doc.total_score]);
-
- bars.push({
- 'title': title,
- 'width': (frm.doc.total_score_obtained / frm.doc.total_score * 100) + '%',
- 'progress_class': 'progress-bar-success'
- });
- if (bars[0].width == '0%') {
- bars[0].width = '0.5%';
- added_min = 0.5;
- }
- message = title;
- frm.dashboard.add_progress(__('Status'), bars, message);
- },
-});
-
-frappe.ui.form.on('Patient Assessment Sheet', {
- score: function(frm, cdt, cdn) {
- frm.events.calculate_total_score(frm, cdt, cdn);
- }
-});
diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json
deleted file mode 100644
index eb0021f..0000000
--- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json
+++ /dev/null
@@ -1,181 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2020-04-19 22:45:12.356209",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "therapy_session",
- "patient",
- "assessment_template",
- "column_break_4",
- "company",
- "healthcare_practitioner",
- "assessment_datetime",
- "assessment_description",
- "section_break_7",
- "assessment_sheet",
- "section_break_9",
- "total_score_obtained",
- "column_break_11",
- "total_score",
- "scale_min",
- "scale_max",
- "amended_from"
- ],
- "fields": [
- {
- "fetch_from": "therapy_session.patient",
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fieldname": "assessment_template",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Assessment Template",
- "options": "Patient Assessment Template",
- "reqd": 1
- },
- {
- "fieldname": "therapy_session",
- "fieldtype": "Link",
- "label": "Therapy Session",
- "options": "Therapy Session"
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fetch_from": "therapy_session.practitioner",
- "fieldname": "healthcare_practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "assessment_datetime",
- "fieldtype": "Datetime",
- "label": "Assessment Datetime",
- "reqd": 1
- },
- {
- "fieldname": "section_break_7",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "assessment_sheet",
- "fieldtype": "Table",
- "label": "Assessment Sheet",
- "options": "Patient Assessment Sheet"
- },
- {
- "fieldname": "section_break_9",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "total_score",
- "fieldtype": "Int",
- "label": "Total Score",
- "read_only": 1
- },
- {
- "fieldname": "column_break_11",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "total_score_obtained",
- "fieldtype": "Int",
- "label": "Total Score Obtained",
- "read_only": 1
- },
- {
- "fieldname": "scale_min",
- "fieldtype": "Int",
- "hidden": 1,
- "label": "Scale Min",
- "read_only": 1
- },
- {
- "fieldname": "scale_max",
- "fieldtype": "Int",
- "hidden": 1,
- "label": "Scale Max",
- "read_only": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Naming Series",
- "options": "HLC-PA-.YYYY.-"
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Patient Assessment",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "assessment_description",
- "fieldtype": "Small Text",
- "label": "Assessment Description"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company"
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-06-25 00:25:13.208400",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Assessment",
- "owner": "Administrator",
- "permissions": [
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py
deleted file mode 100644
index 3033a3e..0000000
--- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe.model.mapper import get_mapped_doc
-
-class PatientAssessment(Document):
- def validate(self):
- self.set_total_score()
-
- def set_total_score(self):
- total_score = 0
- for entry in self.assessment_sheet:
- total_score += int(entry.score)
- self.total_score_obtained = total_score
-
-@frappe.whitelist()
-def create_patient_assessment(source_name, target_doc=None):
- doc = get_mapped_doc('Therapy Session', source_name, {
- 'Therapy Session': {
- 'doctype': 'Patient Assessment',
- 'field_map': [
- ['therapy_session', 'name'],
- ['patient', 'patient'],
- ['practitioner', 'practitioner']
- ]
- }
- }, target_doc)
-
- return doc
-
-
-
diff --git a/erpnext/healthcare/doctype/patient_assessment/test_patient_assessment.py b/erpnext/healthcare/doctype/patient_assessment/test_patient_assessment.py
deleted file mode 100644
index 3fda855..0000000
--- a/erpnext/healthcare/doctype/patient_assessment/test_patient_assessment.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestPatientAssessment(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/patient_assessment_detail/__init__.py b/erpnext/healthcare/doctype/patient_assessment_detail/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_detail/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_assessment_detail/patient_assessment_detail.json b/erpnext/healthcare/doctype/patient_assessment_detail/patient_assessment_detail.json
deleted file mode 100644
index 179f441..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_detail/patient_assessment_detail.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "actions": [],
- "creation": "2020-04-19 19:33:00.115395",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "assessment_parameter"
- ],
- "fields": [
- {
- "fieldname": "assessment_parameter",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Assessment Parameter",
- "options": "Patient Assessment Parameter",
- "reqd": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-04-19 19:33:00.115395",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Assessment Detail",
- "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/healthcare/doctype/patient_assessment_detail/patient_assessment_detail.py b/erpnext/healthcare/doctype/patient_assessment_detail/patient_assessment_detail.py
deleted file mode 100644
index 0519599..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_detail/patient_assessment_detail.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientAssessmentDetail(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_assessment_parameter/__init__.py b/erpnext/healthcare/doctype/patient_assessment_parameter/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_parameter/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.js b/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.js
deleted file mode 100644
index 2c5d270..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient Assessment Parameter', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.json b/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.json
deleted file mode 100644
index 098bdef..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
- "actions": [],
- "autoname": "field:assessment_parameter",
- "creation": "2020-04-15 14:34:46.551042",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "assessment_parameter"
- ],
- "fields": [
- {
- "fieldname": "assessment_parameter",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Assessment Parameter",
- "reqd": 1,
- "unique": 1
- }
- ],
- "links": [],
- "modified": "2020-04-20 09:22:19.135196",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Assessment Parameter",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.py b/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.py
deleted file mode 100644
index b8e0074..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_parameter/patient_assessment_parameter.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientAssessmentParameter(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_assessment_parameter/test_patient_assessment_parameter.py b/erpnext/healthcare/doctype/patient_assessment_parameter/test_patient_assessment_parameter.py
deleted file mode 100644
index e722f99..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_parameter/test_patient_assessment_parameter.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestPatientAssessmentParameter(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/patient_assessment_sheet/__init__.py b/erpnext/healthcare/doctype/patient_assessment_sheet/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_sheet/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_assessment_sheet/patient_assessment_sheet.json b/erpnext/healthcare/doctype/patient_assessment_sheet/patient_assessment_sheet.json
deleted file mode 100644
index 64e4aef..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_sheet/patient_assessment_sheet.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "actions": [],
- "creation": "2020-04-19 23:07:02.220244",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "parameter",
- "score",
- "time",
- "column_break_4",
- "comments"
- ],
- "fields": [
- {
- "fieldname": "parameter",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Parameter",
- "options": "Patient Assessment Parameter",
- "reqd": 1
- },
- {
- "fieldname": "score",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Score",
- "reqd": 1
- },
- {
- "fieldname": "time",
- "fieldtype": "Time",
- "label": "Time"
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "comments",
- "fieldtype": "Small Text",
- "label": "Comments"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-04-20 09:56:28.746619",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Assessment Sheet",
- "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/healthcare/doctype/patient_assessment_sheet/patient_assessment_sheet.py b/erpnext/healthcare/doctype/patient_assessment_sheet/patient_assessment_sheet.py
deleted file mode 100644
index 40da763..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_sheet/patient_assessment_sheet.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientAssessmentSheet(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_assessment_template/__init__.py b/erpnext/healthcare/doctype/patient_assessment_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.js b/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.js
deleted file mode 100644
index 4041936..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient Assessment Template', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.json b/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.json
deleted file mode 100644
index de006b1..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
- "actions": [],
- "autoname": "field:assessment_name",
- "creation": "2020-04-19 19:33:13.204707",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "assessment_name",
- "section_break_2",
- "parameters",
- "assessment_scale_details_section",
- "scale_min",
- "scale_max",
- "column_break_8",
- "assessment_description"
- ],
- "fields": [
- {
- "fieldname": "parameters",
- "fieldtype": "Table",
- "label": "Parameters",
- "options": "Patient Assessment Detail"
- },
- {
- "fieldname": "assessment_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Assessment Name",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "section_break_2",
- "fieldtype": "Section Break",
- "label": "Assessment Parameters"
- },
- {
- "fieldname": "assessment_scale_details_section",
- "fieldtype": "Section Break",
- "label": "Assessment Scale"
- },
- {
- "fieldname": "scale_min",
- "fieldtype": "Int",
- "label": "Scale Minimum"
- },
- {
- "fieldname": "scale_max",
- "fieldtype": "Int",
- "label": "Scale Maximum"
- },
- {
- "fieldname": "column_break_8",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "assessment_description",
- "fieldtype": "Small Text",
- "label": "Assessment Description"
- }
- ],
- "links": [],
- "modified": "2020-04-21 13:14:19.075167",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Assessment Template",
- "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": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.py b/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.py
deleted file mode 100644
index 083cab5..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_template/patient_assessment_template.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientAssessmentTemplate(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_assessment_template/test_patient_assessment_template.py b/erpnext/healthcare/doctype/patient_assessment_template/test_patient_assessment_template.py
deleted file mode 100644
index 86dbd54..0000000
--- a/erpnext/healthcare/doctype/patient_assessment_template/test_patient_assessment_template.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestPatientAssessmentTemplate(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/patient_encounter/__init__.py b/erpnext/healthcare/doctype/patient_encounter/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
deleted file mode 100644
index aaeaa69..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient Encounter', {
- setup: function(frm) {
- frm.get_field('therapies').grid.editable_fields = [
- {fieldname: 'therapy_type', columns: 8},
- {fieldname: 'no_of_sessions', columns: 2}
- ];
- frm.get_field('drug_prescription').grid.editable_fields = [
- {fieldname: 'drug_code', columns: 2},
- {fieldname: 'drug_name', columns: 2},
- {fieldname: 'dosage', columns: 2},
- {fieldname: 'period', columns: 2}
- ];
- frm.get_field('lab_test_prescription').grid.editable_fields = [
- {fieldname: 'lab_test_code', columns: 2},
- {fieldname: 'lab_test_name', columns: 4},
- {fieldname: 'lab_test_comment', columns: 4}
- ];
- },
-
- refresh: function(frm) {
- refresh_field('drug_prescription');
- refresh_field('lab_test_prescription');
-
- if (!frm.doc.__islocal) {
- if (frm.doc.docstatus === 1) {
- if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') {
- frm.add_custom_button(__('Schedule Discharge'), function() {
- schedule_discharge(frm);
- });
- } else if (frm.doc.inpatient_status != 'Discharge Scheduled') {
- frm.add_custom_button(__('Schedule Admission'), function() {
- schedule_inpatient(frm);
- });
- }
- }
-
- frm.add_custom_button(__('Patient History'), function() {
- if (frm.doc.patient) {
- frappe.route_options = {'patient': frm.doc.patient};
- frappe.set_route('patient_history');
- } else {
- frappe.msgprint(__('Please select Patient'));
- }
- },'View');
-
- frm.add_custom_button(__('Vital Signs'), function() {
- create_vital_signs(frm);
- },'Create');
-
- frm.add_custom_button(__('Medical Record'), function() {
- create_medical_record(frm);
- },'Create');
-
- frm.add_custom_button(__('Clinical Procedure'), function() {
- create_procedure(frm);
- },'Create');
-
- if (frm.doc.drug_prescription && frm.doc.inpatient_record && frm.doc.inpatient_status === "Admitted") {
- frm.add_custom_button(__('Inpatient Medication Order'), function() {
- frappe.model.open_mapped_doc({
- method: 'erpnext.healthcare.doctype.patient_encounter.patient_encounter.make_ip_medication_order',
- frm: frm
- });
- }, 'Create');
- }
- }
-
- frm.set_query('patient', function() {
- return {
- filters: {'status': 'Active'}
- };
- });
-
- frm.set_query('drug_code', 'drug_prescription', function() {
- return {
- filters: {
- is_stock_item: 1
- }
- };
- });
-
- frm.set_query('lab_test_code', 'lab_test_prescription', function() {
- return {
- filters: {
- is_billable: 1
- }
- };
- });
-
- frm.set_query('appointment', function() {
- return {
- filters: {
- // Scheduled filter for demo ...
- status:['in',['Open','Scheduled']]
- }
- };
- });
-
- frm.set_df_property('patient', 'read_only', frm.doc.appointment ? 1 : 0);
- },
-
- appointment: function(frm) {
- frm.events.set_appointment_fields(frm);
- },
-
- patient: function(frm) {
- frm.events.set_patient_info(frm);
- },
-
- practitioner: function(frm) {
- if (!frm.doc.practitioner) {
- frm.set_value('practitioner_name', '');
- }
- },
- set_appointment_fields: function(frm) {
- if (frm.doc.appointment) {
- frappe.call({
- method: 'frappe.client.get',
- args: {
- doctype: 'Patient Appointment',
- name: frm.doc.appointment
- },
- callback: function(data) {
- let values = {
- 'patient':data.message.patient,
- 'type': data.message.appointment_type,
- 'practitioner': data.message.practitioner,
- 'invoiced': data.message.invoiced,
- 'company': data.message.company
- };
- frm.set_value(values);
- frm.set_df_property('patient', 'read_only', 1);
- }
- });
- }
- else {
- let values = {
- 'patient': '',
- 'patient_name': '',
- 'type': '',
- 'practitioner': '',
- 'invoiced': 0,
- 'patient_sex': '',
- 'patient_age': '',
- 'inpatient_record': '',
- 'inpatient_status': ''
- };
- frm.set_value(values);
- frm.set_df_property('patient', 'read_only', 0);
- }
- },
-
- set_patient_info: function(frm) {
- if (frm.doc.patient) {
- frappe.call({
- method: 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
- args: {
- patient: frm.doc.patient
- },
- callback: function(data) {
- let age = '';
- if (data.message.dob) {
- age = calculate_age(data.message.dob);
- }
- let values = {
- 'patient_age': age,
- 'patient_name':data.message.patient_name,
- 'patient_sex': data.message.sex,
- 'inpatient_record': data.message.inpatient_record,
- 'inpatient_status': data.message.inpatient_status
- };
- frm.set_value(values);
- }
- });
- } else {
- let values = {
- 'patient_age': '',
- 'patient_name':'',
- 'patient_sex': '',
- 'inpatient_record': '',
- 'inpatient_status': ''
- };
- frm.set_value(values);
- }
- }
-});
-
-var schedule_inpatient = function(frm) {
- var dialog = new frappe.ui.Dialog({
- title: 'Patient Admission',
- fields: [
- {fieldtype: 'Link', label: 'Medical Department', fieldname: 'medical_department', options: 'Medical Department', reqd: 1},
- {fieldtype: 'Link', label: 'Healthcare Practitioner (Primary)', fieldname: 'primary_practitioner', options: 'Healthcare Practitioner', reqd: 1},
- {fieldtype: 'Link', label: 'Healthcare Practitioner (Secondary)', fieldname: 'secondary_practitioner', options: 'Healthcare Practitioner'},
- {fieldtype: 'Column Break'},
- {fieldtype: 'Date', label: 'Admission Ordered For', fieldname: 'admission_ordered_for', default: 'Today'},
- {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'},
- {fieldtype: 'Int', label: 'Expected Length of Stay', fieldname: 'expected_length_of_stay'},
- {fieldtype: 'Section Break'},
- {fieldtype: 'Long Text', label: 'Admission Instructions', fieldname: 'admission_instruction'}
- ],
- primary_action_label: __('Order Admission'),
- primary_action : function() {
- var args = {
- patient: frm.doc.patient,
- admission_encounter: frm.doc.name,
- referring_practitioner: frm.doc.practitioner,
- company: frm.doc.company,
- medical_department: dialog.get_value('medical_department'),
- primary_practitioner: dialog.get_value('primary_practitioner'),
- secondary_practitioner: dialog.get_value('secondary_practitioner'),
- admission_ordered_for: dialog.get_value('admission_ordered_for'),
- admission_service_unit_type: dialog.get_value('service_unit_type'),
- expected_length_of_stay: dialog.get_value('expected_length_of_stay'),
- admission_instruction: dialog.get_value('admission_instruction')
- }
- frappe.call({
- method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient',
- args: {
- args: args
- },
- callback: function(data) {
- if (!data.exc) {
- frm.reload_doc();
- }
- },
- freeze: true,
- freeze_message: __('Scheduling Patient Admission')
- });
- frm.refresh_fields();
- dialog.hide();
- }
- });
-
- dialog.set_values({
- 'medical_department': frm.doc.medical_department,
- 'primary_practitioner': frm.doc.practitioner,
- });
-
- dialog.fields_dict['service_unit_type'].get_query = function() {
- return {
- filters: {
- 'inpatient_occupancy': 1,
- 'allow_appointments': 0
- }
- };
- };
-
- dialog.show();
- dialog.$wrapper.find('.modal-dialog').css('width', '800px');
-};
-
-var schedule_discharge = function(frm) {
- var dialog = new frappe.ui.Dialog ({
- title: 'Inpatient Discharge',
- fields: [
- {fieldtype: 'Date', label: 'Discharge Ordered Date', fieldname: 'discharge_ordered_date', default: 'Today', read_only: 1},
- {fieldtype: 'Date', label: 'Followup Date', fieldname: 'followup_date'},
- {fieldtype: 'Column Break'},
- {fieldtype: 'Small Text', label: 'Discharge Instructions', fieldname: 'discharge_instructions'},
- {fieldtype: 'Section Break', label:'Discharge Summary'},
- {fieldtype: 'Long Text', label: 'Discharge Note', fieldname: 'discharge_note'}
- ],
- primary_action_label: __('Order Discharge'),
- primary_action : function() {
- var args = {
- patient: frm.doc.patient,
- discharge_encounter: frm.doc.name,
- discharge_practitioner: frm.doc.practitioner,
- discharge_ordered_date: dialog.get_value('discharge_ordered_date'),
- followup_date: dialog.get_value('followup_date'),
- discharge_instructions: dialog.get_value('discharge_instructions'),
- discharge_note: dialog.get_value('discharge_note')
- }
- frappe.call ({
- method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge',
- args: {args},
- callback: function(data) {
- if(!data.exc){
- frm.reload_doc();
- }
- },
- freeze: true,
- freeze_message: 'Scheduling Inpatient Discharge'
- });
- frm.refresh_fields();
- dialog.hide();
- }
- });
-
- dialog.show();
- dialog.$wrapper.find('.modal-dialog').css('width', '800px');
-};
-
-let create_medical_record = function(frm) {
- if (!frm.doc.patient) {
- frappe.throw(__('Please select patient'));
- }
- frappe.route_options = {
- 'patient': frm.doc.patient,
- 'status': 'Open',
- 'reference_doctype': 'Patient Medical Record',
- 'reference_owner': frm.doc.owner
- };
- frappe.new_doc('Patient Medical Record');
-};
-
-let create_vital_signs = function(frm) {
- if (!frm.doc.patient) {
- frappe.throw(__('Please select patient'));
- }
- frappe.route_options = {
- 'patient': frm.doc.patient,
- 'encounter': frm.doc.name,
- 'company': frm.doc.company
- };
- frappe.new_doc('Vital Signs');
-};
-
-let create_procedure = function(frm) {
- if (!frm.doc.patient) {
- frappe.throw(__('Please select patient'));
- }
- frappe.route_options = {
- 'patient': frm.doc.patient,
- 'medical_department': frm.doc.medical_department,
- 'company': frm.doc.company
- };
- frappe.new_doc('Clinical Procedure');
-};
-
-frappe.ui.form.on('Drug Prescription', {
- dosage: function(frm, cdt, cdn){
- frappe.model.set_value(cdt, cdn, 'update_schedule', 1);
- let child = locals[cdt][cdn];
- if (child.dosage) {
- frappe.model.set_value(cdt, cdn, 'interval_uom', 'Day');
- frappe.model.set_value(cdt, cdn, 'interval', 1);
- }
- },
- period: function(frm, cdt, cdn) {
- frappe.model.set_value(cdt, cdn, 'update_schedule', 1);
- },
- interval_uom: function(frm, cdt, cdn) {
- frappe.model.set_value(cdt, cdn, 'update_schedule', 1);
- let child = locals[cdt][cdn];
- if (child.interval_uom == 'Hour') {
- frappe.model.set_value(cdt, cdn, 'dosage', null);
- }
- }
-});
-
-let calculate_age = function(birth) {
- let ageMS = Date.parse(Date()) - Date.parse(birth);
- let age = new Date();
- age.setTime(ageMS);
- let years = age.getFullYear() - 1970;
- return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
-};
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
deleted file mode 100644
index b646ff9..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
+++ /dev/null
@@ -1,361 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2016-04-21 10:53:44.637684",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "title",
- "appointment",
- "appointment_type",
- "patient",
- "patient_name",
- "patient_sex",
- "patient_age",
- "inpatient_record",
- "inpatient_status",
- "column_break_6",
- "company",
- "encounter_date",
- "encounter_time",
- "practitioner",
- "practitioner_name",
- "medical_department",
- "invoiced",
- "sb_symptoms",
- "symptoms",
- "symptoms_in_print",
- "physical_examination",
- "diagnosis",
- "diagnosis_in_print",
- "codification",
- "codification_table",
- "sb_drug_prescription",
- "drug_prescription",
- "sb_test_prescription",
- "lab_test_prescription",
- "sb_procedures",
- "procedure_prescription",
- "rehabilitation_section",
- "therapy_plan",
- "therapies",
- "section_break_33",
- "encounter_comment",
- "sb_refs",
- "amended_from"
- ],
- "fields": [
- {
- "allow_on_submit": 1,
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "no_copy": 1,
- "options": "HLC-ENC-.YYYY.-",
- "set_only_once": 1
- },
- {
- "fieldname": "appointment",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Appointment",
- "options": "Patient Appointment",
- "search_index": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "label": "Age",
- "read_only": 1
- },
- {
- "fieldname": "patient_sex",
- "fieldtype": "Link",
- "label": "Gender",
- "options": "Gender",
- "read_only": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company"
- },
- {
- "fieldname": "column_break_6",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "reqd": 1
- },
- {
- "default": "Today",
- "fieldname": "encounter_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Encounter Date",
- "reqd": 1
- },
- {
- "fieldname": "encounter_time",
- "fieldtype": "Time",
- "label": "Encounter Time",
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "sb_symptoms",
- "fieldtype": "Section Break",
- "label": "Encounter Impression"
- },
- {
- "fieldname": "symptoms",
- "fieldtype": "Table MultiSelect",
- "ignore_xss_filter": 1,
- "label": "Symptoms",
- "no_copy": 1,
- "options": "Patient Encounter Symptom"
- },
- {
- "default": "0",
- "depends_on": "eval: doc.symptoms != ''",
- "fieldname": "symptoms_in_print",
- "fieldtype": "Check",
- "label": "In print",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "physical_examination",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "diagnosis",
- "fieldtype": "Table MultiSelect",
- "ignore_xss_filter": 1,
- "label": "Diagnosis",
- "no_copy": 1,
- "options": "Patient Encounter Diagnosis"
- },
- {
- "default": "1",
- "depends_on": "eval: doc.diagnosis != ''",
- "fieldname": "diagnosis_in_print",
- "fieldtype": "Check",
- "label": "In print",
- "no_copy": 1,
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "codification",
- "fieldtype": "Section Break",
- "label": "Medical Coding"
- },
- {
- "fieldname": "codification_table",
- "fieldtype": "Table",
- "label": "Medical Codes",
- "options": "Codification Table"
- },
- {
- "fieldname": "sb_drug_prescription",
- "fieldtype": "Section Break",
- "label": "Medications"
- },
- {
- "fieldname": "drug_prescription",
- "fieldtype": "Table",
- "label": "Drug Prescription",
- "options": "Drug Prescription"
- },
- {
- "fieldname": "sb_test_prescription",
- "fieldtype": "Section Break",
- "label": "Investigations"
- },
- {
- "fieldname": "lab_test_prescription",
- "fieldtype": "Table",
- "label": "Lab Tests",
- "options": "Lab Prescription"
- },
- {
- "fieldname": "sb_procedures",
- "fieldtype": "Section Break",
- "label": "Procedures"
- },
- {
- "fieldname": "procedure_prescription",
- "fieldtype": "Table",
- "label": "Clinical Procedures",
- "no_copy": 1,
- "options": "Procedure Prescription"
- },
- {
- "fieldname": "encounter_comment",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Review Details",
- "no_copy": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Patient Encounter",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "rehabilitation_section",
- "fieldtype": "Section Break",
- "label": "Rehabilitation"
- },
- {
- "fieldname": "therapies",
- "fieldtype": "Table",
- "label": "Therapies",
- "options": "Therapy Plan Detail"
- },
- {
- "fieldname": "section_break_33",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "therapy_plan",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Therapy Plan",
- "options": "Therapy Plan",
- "read_only": 1
- },
- {
- "fieldname": "appointment_type",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Appointment Type",
- "no_copy": 1,
- "options": "Appointment Type",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fetch_from": "practitioner.department",
- "fieldname": "medical_department",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Department",
- "options": "Medical Department",
- "read_only": 1
- },
- {
- "allow_on_submit": 1,
- "fieldname": "inpatient_status",
- "fieldtype": "Data",
- "label": "Inpatient Status",
- "read_only": 1
- },
- {
- "fieldname": "sb_refs",
- "fieldtype": "Section Break"
- },
- {
- "fetch_from": "practitioner.practitioner_name",
- "fieldname": "practitioner_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Practitioner Name",
- "read_only": 1
- },
- {
- "allow_on_submit": 1,
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-11-30 10:39:00.783119",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Encounter",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient, practitioner, medical_department, encounter_date, encounter_time",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
deleted file mode 100644
index cc21417..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import cstr, getdate, add_days
-from frappe import _
-from frappe.model.mapper import get_mapped_doc
-
-class PatientEncounter(Document):
- def validate(self):
- self.set_title()
-
- def on_update(self):
- if self.appointment:
- frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
-
- def on_submit(self):
- if self.therapies:
- create_therapy_plan(self)
-
- def on_cancel(self):
- if self.appointment:
- frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
-
- if self.inpatient_record and self.drug_prescription:
- delete_ip_medication_order(self)
-
- def set_title(self):
- self.title = _('{0} with {1}').format(self.patient_name or self.patient,
- self.practitioner_name or self.practitioner)[:100]
-
-@frappe.whitelist()
-def make_ip_medication_order(source_name, target_doc=None):
- def set_missing_values(source, target):
- target.start_date = source.encounter_date
- for entry in source.drug_prescription:
- if entry.drug_code:
- dosage = frappe.get_doc('Prescription Dosage', entry.dosage)
- dates = get_prescription_dates(entry.period, target.start_date)
- for date in dates:
- for dose in dosage.dosage_strength:
- order = target.append('medication_orders')
- order.drug = entry.drug_code
- order.drug_name = entry.drug_name
- order.dosage = dose.strength
- order.instructions = entry.comment
- order.dosage_form = entry.dosage_form
- order.date = date
- order.time = dose.strength_time
- target.end_date = dates[-1]
-
- doc = get_mapped_doc('Patient Encounter', source_name, {
- 'Patient Encounter': {
- 'doctype': 'Inpatient Medication Order',
- 'field_map': {
- 'name': 'patient_encounter',
- 'patient': 'patient',
- 'patient_name': 'patient_name',
- 'patient_age': 'patient_age',
- 'inpatient_record': 'inpatient_record',
- 'practitioner': 'practitioner',
- 'start_date': 'encounter_date'
- },
- }
- }, target_doc, set_missing_values)
-
- return doc
-
-
-def get_prescription_dates(period, start_date):
- prescription_duration = frappe.get_doc('Prescription Duration', period)
- days = prescription_duration.get_days()
- dates = [start_date]
- for i in range(1, days):
- dates.append(add_days(getdate(start_date), i))
- return dates
-
-
-def create_therapy_plan(encounter):
- if len(encounter.therapies):
- doc = frappe.new_doc('Therapy Plan')
- doc.patient = encounter.patient
- doc.start_date = encounter.encounter_date
- for entry in encounter.therapies:
- doc.append('therapy_plan_details', {
- 'therapy_type': entry.therapy_type,
- 'no_of_sessions': entry.no_of_sessions
- })
- doc.save(ignore_permissions=True)
- if doc.get('name'):
- encounter.db_set('therapy_plan', doc.name)
- frappe.msgprint(_('Therapy Plan {0} created successfully.').format(frappe.bold(doc.name)), alert=True)
-
-
-def delete_ip_medication_order(encounter):
- record = frappe.db.exists('Inpatient Medication Order', {'patient_encounter': encounter.name})
- if record:
- frappe.delete_doc('Inpatient Medication Order', record, force=1)
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter_dashboard.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter_dashboard.py
deleted file mode 100644
index 39e54f5..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter_dashboard.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'encounter',
- 'non_standard_fieldnames': {
- 'Patient Medical Record': 'reference_name',
- 'Inpatient Medication Order': 'patient_encounter'
- },
- 'transactions': [
- {
- 'label': _('Records'),
- 'items': ['Vital Signs', 'Patient Medical Record']
- },
- {
- 'label': _('Orders'),
- 'items': ['Inpatient Medication Order']
- }
- ],
- 'disable_create_buttons': ['Inpatient Medication Order']
- }
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js
deleted file mode 100644
index d8f63bd..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter_list.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
-(c) ESS 2015-16
-*/
-frappe.listview_settings['Patient Encounter'] = {
- filters:[["docstatus","!=","2"]]
-};
diff --git a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.js
deleted file mode 100644
index 1baabf7..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient Encounter", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Patient Encounter
- () => frappe.tests.make('Patient Encounter', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py
deleted file mode 100644
index f5df152..0000000
--- a/erpnext/healthcare/doctype/patient_encounter/test_patient_encounter.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestPatientEncounter(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/patient_encounter_diagnosis/__init__.py b/erpnext/healthcare/doctype/patient_encounter_diagnosis/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_encounter_diagnosis/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.json b/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.json
deleted file mode 100644
index 00ca309..0000000
--- a/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "actions": [],
- "beta": 1,
- "creation": "2020-02-26 16:48:16.835105",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "diagnosis"
- ],
- "fields": [
- {
- "fieldname": "diagnosis",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Diagnosis",
- "options": "Diagnosis",
- "reqd": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-02-26 16:58:16.480583",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Encounter Diagnosis",
- "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/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.py b/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.py
deleted file mode 100644
index 34b0cf8..0000000
--- a/erpnext/healthcare/doctype/patient_encounter_diagnosis/patient_encounter_diagnosis.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientEncounterDiagnosis(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_encounter_symptom/__init__.py b/erpnext/healthcare/doctype/patient_encounter_symptom/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_encounter_symptom/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.json b/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.json
deleted file mode 100644
index bc92145..0000000
--- a/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "actions": [],
- "beta": 1,
- "creation": "2020-02-26 16:47:00.525657",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "complaint"
- ],
- "fields": [
- {
- "fieldname": "complaint",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Complaint",
- "options": "Complaint",
- "reqd": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-02-26 16:57:37.997481",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Encounter Symptom",
- "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/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.py b/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.py
deleted file mode 100644
index bdb7bb2..0000000
--- a/erpnext/healthcare/doctype/patient_encounter_symptom/patient_encounter_symptom.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientEncounterSymptom(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json
deleted file mode 100644
index 3025c7b..0000000
--- a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "actions": [],
- "creation": "2020-11-25 13:40:23.054469",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "document_type",
- "date_fieldname",
- "add_edit_fields",
- "selected_fields"
- ],
- "fields": [
- {
- "fieldname": "document_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Document Type",
- "options": "DocType",
- "reqd": 1
- },
- {
- "fieldname": "selected_fields",
- "fieldtype": "Code",
- "label": "Selected Fields",
- "read_only": 1
- },
- {
- "fieldname": "add_edit_fields",
- "fieldtype": "Button",
- "in_list_view": 1,
- "label": "Add / Edit Fields"
- },
- {
- "fieldname": "date_fieldname",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Date Fieldname",
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2020-11-30 13:54:37.474671",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient History Custom Document Type",
- "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/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py
deleted file mode 100644
index f0a1f92..0000000
--- a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientHistoryCustomDocumentType(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_history_settings/__init__.py b/erpnext/healthcare/doctype/patient_history_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_history_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js
deleted file mode 100644
index 453da6a..0000000
--- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient History Settings', {
- refresh: function(frm) {
- frm.set_query('document_type', 'custom_doctypes', () => {
- return {
- filters: {
- custom: 1,
- is_submittable: 1,
- module: 'Healthcare',
- }
- };
- });
- },
-
- field_selector: function(frm, doc, standard=1) {
- let document_fields = [];
- if (doc.selected_fields)
- document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname);
-
- frm.call({
- method: 'get_doctype_fields',
- doc: frm.doc,
- args: {
- document_type: doc.document_type,
- fields: document_fields
- },
- freeze: true,
- callback: function(r) {
- if (r.message) {
- let doctype = 'Patient History Custom Document Type';
- if (standard)
- doctype = 'Patient History Standard Document Type';
-
- frm.events.show_field_selector_dialog(frm, doc, doctype, r.message);
- }
- }
- });
- },
-
- show_field_selector_dialog: function(frm, doc, doctype, doc_fields) {
- let d = new frappe.ui.Dialog({
- title: __('{0} Fields', [__(doc.document_type)]),
- fields: [
- {
- label: __('Select Fields'),
- fieldtype: 'MultiCheck',
- fieldname: 'fields',
- options: doc_fields,
- columns: 2
- }
- ]
- });
-
- d.$body.prepend(`
- <div class="columns-search">
- <input type="text" placeholder="${__('Search')}" data-element="search" class="form-control input-xs">
- </div>`
- );
-
- frappe.utils.setup_search(d.$body, '.unit-checkbox', '.label-area');
-
- d.set_primary_action(__('Save'), () => {
- let values = d.get_values().fields;
-
- let selected_fields = [];
-
- frappe.model.with_doctype(doc.document_type, function() {
- for (let idx in values) {
- let value = values[idx];
-
- let field = frappe.get_meta(doc.document_type).fields.filter((df) => df.fieldname == value)[0];
- if (field) {
- selected_fields.push({
- label: field.label,
- fieldname: field.fieldname,
- fieldtype: field.fieldtype
- });
- }
- }
-
- d.refresh();
- frappe.model.set_value(doctype, doc.name, 'selected_fields', JSON.stringify(selected_fields));
- });
-
- d.hide();
- });
-
- d.show();
- },
-
- get_date_field_for_dt: function(frm, row) {
- frm.call({
- method: 'get_date_field_for_dt',
- doc: frm.doc,
- args: {
- document_type: row.document_type
- },
- callback: function(data) {
- if (data.message) {
- frappe.model.set_value('Patient History Custom Document Type',
- row.name, 'date_fieldname', data.message);
- }
- }
- });
- }
-});
-
-frappe.ui.form.on('Patient History Custom Document Type', {
- document_type: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- if (row.document_type) {
- frm.events.get_date_field_for_dt(frm, row);
- }
- },
-
- add_edit_fields: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- if (row.document_type) {
- frm.events.field_selector(frm, row, 0);
- }
- }
-});
-
-frappe.ui.form.on('Patient History Standard Document Type', {
- add_edit_fields: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- if (row.document_type) {
- frm.events.field_selector(frm, row);
- }
- }
-});
diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json
deleted file mode 100644
index 143e2c9..0000000
--- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "actions": [],
- "creation": "2020-11-25 13:41:37.675518",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "standard_doctypes",
- "section_break_2",
- "custom_doctypes"
- ],
- "fields": [
- {
- "fieldname": "section_break_2",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "custom_doctypes",
- "fieldtype": "Table",
- "label": "Custom Document Types",
- "options": "Patient History Custom Document Type"
- },
- {
- "fieldname": "standard_doctypes",
- "fieldtype": "Table",
- "label": "Standard Document Types",
- "options": "Patient History Standard Document Type",
- "read_only": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "issingle": 1,
- "links": [],
- "modified": "2020-11-25 13:43:38.511771",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient History Settings",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py
deleted file mode 100644
index 887d58a..0000000
--- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe import _
-from frappe.utils import cstr, cint
-from frappe.model.document import Document
-from erpnext.healthcare.page.patient_history.patient_history import get_patient_history_doctypes
-
-class PatientHistorySettings(Document):
- def validate(self):
- self.validate_submittable_doctypes()
- self.validate_date_fieldnames()
-
- def validate_submittable_doctypes(self):
- for entry in self.custom_doctypes:
- if not cint(frappe.db.get_value('DocType', entry.document_type, 'is_submittable')):
- msg = _('Row #{0}: Document Type {1} is not submittable. ').format(
- entry.idx, frappe.bold(entry.document_type))
- msg += _('Patient Medical Record can only be created for submittable document types.')
- frappe.throw(msg)
-
- def validate_date_fieldnames(self):
- for entry in self.custom_doctypes:
- field = frappe.get_meta(entry.document_type).get_field(entry.date_fieldname)
- if not field:
- frappe.throw(_('Row #{0}: No such Field named {1} found in the Document Type {2}.').format(
- entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
-
- if field.fieldtype not in ['Date', 'Datetime']:
- frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format(
- entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
-
- @frappe.whitelist()
- def get_doctype_fields(self, document_type, fields):
- multicheck_fields = []
- doc_fields = frappe.get_meta(document_type).fields
-
- for field in doc_fields:
- if field.fieldtype not in frappe.model.no_value_fields or \
- field.fieldtype in frappe.model.table_fields and not field.hidden:
- multicheck_fields.append({
- 'label': field.label,
- 'value': field.fieldname,
- 'checked': 1 if field.fieldname in fields else 0
- })
-
- return multicheck_fields
-
- @frappe.whitelist()
- def get_date_field_for_dt(self, document_type):
- meta = frappe.get_meta(document_type)
- date_fields = meta.get('fields', {
- 'fieldtype': ['in', ['Date', 'Datetime']]
- })
-
- if date_fields:
- return date_fields[0].get('fieldname')
-
-def create_medical_record(doc, method=None):
- medical_record_required = validate_medical_record_required(doc)
- if not medical_record_required:
- return
-
- if frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }):
- return
-
- subject = set_subject_field(doc)
- date_field = get_date_field(doc.doctype)
- medical_record = frappe.new_doc('Patient Medical Record')
- medical_record.patient = doc.patient
- medical_record.subject = subject
- medical_record.status = 'Open'
- medical_record.communication_date = doc.get(date_field)
- medical_record.reference_doctype = doc.doctype
- medical_record.reference_name = doc.name
- medical_record.reference_owner = doc.owner
- medical_record.save(ignore_permissions=True)
-
-
-def update_medical_record(doc, method=None):
- medical_record_required = validate_medical_record_required(doc)
- if not medical_record_required:
- return
-
- medical_record_id = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name })
-
- if medical_record_id:
- subject = set_subject_field(doc)
- frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject)
- else:
- create_medical_record(doc)
-
-
-def delete_medical_record(doc, method=None):
- medical_record_required = validate_medical_record_required(doc)
- if not medical_record_required:
- return
-
- record = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name })
- if record:
- frappe.delete_doc('Patient Medical Record', record, force=1)
-
-
-def set_subject_field(doc):
- from frappe.utils.formatters import format_value
-
- meta = frappe.get_meta(doc.doctype)
- subject = ''
- patient_history_fields = get_patient_history_fields(doc)
-
- for entry in patient_history_fields:
- fieldname = entry.get('fieldname')
- if entry.get('fieldtype') == 'Table' and doc.get(fieldname):
- formatted_value = get_formatted_value_for_table_field(doc.get(fieldname), meta.get_field(fieldname))
- subject += frappe.bold(_(entry.get('label')) + ': ') + '<br>' + cstr(formatted_value) + '<br>'
-
- else:
- if doc.get(fieldname):
- formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc)
- subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formatted_value) + '<br>'
-
- return subject
-
-
-def get_date_field(doctype):
- dt = get_patient_history_config_dt(doctype)
-
- return frappe.db.get_value(dt, { 'document_type': doctype }, 'date_fieldname')
-
-
-def get_patient_history_fields(doc):
- dt = get_patient_history_config_dt(doc.doctype)
- patient_history_fields = frappe.db.get_value(dt, { 'document_type': doc.doctype }, 'selected_fields')
-
- if patient_history_fields:
- return json.loads(patient_history_fields)
-
-
-def get_formatted_value_for_table_field(items, df):
- child_meta = frappe.get_meta(df.options)
-
- table_head = ''
- table_row = ''
- html = ''
- create_head = True
- for item in items:
- table_row += '<tr>'
- for cdf in child_meta.fields:
- if cdf.in_list_view:
- if create_head:
- table_head += '<td>' + cdf.label + '</td>'
- if item.get(cdf.fieldname):
- table_row += '<td>' + str(item.get(cdf.fieldname)) + '</td>'
- else:
- table_row += '<td></td>'
- create_head = False
- table_row += '</tr>'
-
- html += "<table class='table table-condensed table-bordered'>" + table_head + table_row + "</table>"
-
- return html
-
-
-def get_patient_history_config_dt(doctype):
- if frappe.db.get_value('DocType', doctype, 'custom'):
- return 'Patient History Custom Document Type'
- else:
- return 'Patient History Standard Document Type'
-
-
-def validate_medical_record_required(doc):
- if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard \
- or get_module(doc) != 'Healthcare':
- return False
-
- if doc.doctype not in get_patient_history_doctypes():
- return False
-
- return True
-
-def get_module(doc):
- module = doc.meta.module
- if not module:
- module = frappe.db.get_value('DocType', doc.doctype, 'module')
-
- return module
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py
deleted file mode 100644
index 33119d8..0000000
--- a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-import json
-from frappe.utils import getdate, strip_html
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
-
-class TestPatientHistorySettings(unittest.TestCase):
- def setUp(self):
- dt = create_custom_doctype()
- settings = frappe.get_single("Patient History Settings")
- settings.append("custom_doctypes", {
- "document_type": dt.name,
- "date_fieldname": "date",
- "selected_fields": json.dumps([{
- "label": "Date",
- "fieldname": "date",
- "fieldtype": "Date"
- },
- {
- "label": "Rating",
- "fieldname": "rating",
- "fieldtype": "Rating"
- },
- {
- "label": "Feedback",
- "fieldname": "feedback",
- "fieldtype": "Small Text"
- }])
- })
- settings.save()
-
- def test_custom_doctype_medical_record(self):
- # tests for medical record creation of standard doctypes in test_patient_medical_record.py
- patient = create_patient()
- doc = create_doc(patient)
-
- # check for medical record
- medical_rec = frappe.db.exists("Patient Medical Record", {"status": "Open", "reference_name": doc.name})
- self.assertTrue(medical_rec)
-
- medical_rec = frappe.get_doc("Patient Medical Record", medical_rec)
- expected_subject = "Date: {0}Rating: 3Feedback: Test Patient History Settings".format(
- frappe.utils.format_date(getdate()))
- self.assertEqual(strip_html(medical_rec.subject), expected_subject)
- self.assertEqual(medical_rec.patient, patient)
- self.assertEqual(medical_rec.communication_date, getdate())
-
-
-def create_custom_doctype():
- if not frappe.db.exists("DocType", "Test Patient Feedback"):
- doc = frappe.get_doc({
- "doctype": "DocType",
- "module": "Healthcare",
- "custom": 1,
- "is_submittable": 1,
- "fields": [{
- "label": "Date",
- "fieldname": "date",
- "fieldtype": "Date"
- },
- {
- "label": "Patient",
- "fieldname": "patient",
- "fieldtype": "Link",
- "options": "Patient"
- },
- {
- "label": "Rating",
- "fieldname": "rating",
- "fieldtype": "Rating"
- },
- {
- "label": "Feedback",
- "fieldname": "feedback",
- "fieldtype": "Small Text"
- }],
- "permissions": [{
- "role": "System Manager",
- "read": 1
- }],
- "name": "Test Patient Feedback",
- })
- doc.insert()
- return doc
- else:
- return frappe.get_doc("DocType", "Test Patient Feedback")
-
-
-def create_doc(patient):
- doc = frappe.get_doc({
- "doctype": "Test Patient Feedback",
- "patient": patient,
- "date": getdate(),
- "rating": 3,
- "feedback": "Test Patient History Settings"
- }).insert()
- doc.submit()
-
- return doc
diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json
deleted file mode 100644
index b43099c..0000000
--- a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "actions": [],
- "creation": "2020-11-25 13:39:36.014814",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "document_type",
- "date_fieldname",
- "add_edit_fields",
- "selected_fields"
- ],
- "fields": [
- {
- "fieldname": "document_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Document Type",
- "options": "DocType",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "selected_fields",
- "fieldtype": "Code",
- "label": "Selected Fields",
- "read_only": 1
- },
- {
- "fieldname": "add_edit_fields",
- "fieldtype": "Button",
- "in_list_view": 1,
- "label": "Add / Edit Fields"
- },
- {
- "fieldname": "date_fieldname",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Date Fieldname",
- "read_only": 1,
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2020-11-30 13:54:56.773325",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient History Standard Document Type",
- "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/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py
deleted file mode 100644
index 2d94911..0000000
--- a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class PatientHistoryStandardDocumentType(Document):
- pass
diff --git a/erpnext/healthcare/doctype/patient_medical_record/__init__.py b/erpnext/healthcare/doctype/patient_medical_record/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.js b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.js
deleted file mode 100644
index 93ff70e..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Patient Medical Record', {
-});
diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
deleted file mode 100644
index ed82355..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
+++ /dev/null
@@ -1,155 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2016-06-09 11:30:44.972056",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "patient",
- "status",
- "column_break_2",
- "attach",
- "section_break_4",
- "subject",
- "section_break_8",
- "communication_date",
- "reference_doctype",
- "reference_name",
- "column_break_9",
- "reference_owner",
- "user"
- ],
- "fields": [
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-PMR-.YYYY.-",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "search_index": 1
- },
- {
- "fieldname": "column_break_2",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "attach",
- "fieldtype": "Attach",
- "label": "Attach Medical Record"
- },
- {
- "fieldname": "section_break_4",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "subject",
- "fieldtype": "Text Editor",
- "ignore_xss_filter": 1,
- "label": "Subject"
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "label": "Status",
- "options": "Open\nClose",
- "read_only": 1
- },
- {
- "default": "Today",
- "fieldname": "communication_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Datetime",
- "read_only": 1
- },
- {
- "fieldname": "reference_doctype",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Reference DocType",
- "options": "DocType",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "reference_name",
- "fieldtype": "Dynamic Link",
- "in_list_view": 1,
- "label": "Reference Name",
- "options": "reference_doctype",
- "read_only": 1,
- "search_index": 1
- },
- {
- "fetch_from": "reference_name.owner",
- "fieldname": "reference_owner",
- "fieldtype": "Data",
- "label": "Reference Owner",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "default": "__user",
- "fieldname": "user",
- "fieldtype": "Link",
- "label": "User",
- "options": "User",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "column_break_9",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_8",
- "fieldtype": "Section Break"
- }
- ],
- "in_create": 1,
- "links": [],
- "modified": "2020-04-29 12:26:57.679402",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Medical Record",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient, subject, communication_date, reference_doctype, reference_name",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.py
deleted file mode 100644
index 35e42bd..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class PatientMedicalRecord(Document):
- def after_insert(self):
- if self.reference_doctype == "Patient Medical Record" :
- frappe.db.set_value("Patient Medical Record", self.name, "reference_name", self.name)
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js
deleted file mode 100644
index 66dda09..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Patient Medical Record", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Patient Medical Record
- () => frappe.tests.make('Patient Medical Record', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
deleted file mode 100644
index c1d9872..0000000
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-import frappe
-from frappe.utils import nowdate
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment
-from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
-
-class TestPatientMedicalRecord(unittest.TestCase):
- def setUp(self):
- frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
- frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
- make_pos_profile()
-
- def test_medical_record(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
- encounter = create_encounter(appointment)
-
- # check for encounter
- medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': encounter.name})
- self.assertTrue(medical_rec)
-
- vital_signs = create_vital_signs(appointment)
- # check for vital signs
- medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': vital_signs.name})
- self.assertTrue(medical_rec)
-
- appointment = create_appointment(patient, practitioner, nowdate(), invoice=1, procedure_template=1)
- procedure = create_procedure(appointment)
- procedure.start_procedure()
- procedure.complete_procedure()
- # check for clinical procedure
- medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': procedure.name})
- self.assertTrue(medical_rec)
-
- template = create_lab_test_template(medical_department)
- lab_test = create_lab_test(template.name, patient)
- # check for lab test
- medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
- self.assertTrue(medical_rec)
-
-
-def create_procedure(appointment):
- if appointment:
- procedure = frappe.new_doc('Clinical Procedure')
- procedure.procedure_template = appointment.procedure_template
- procedure.appointment = appointment.name
- procedure.patient = appointment.patient
- procedure.practitioner = appointment.practitioner
- procedure.medical_department = appointment.department
- procedure.start_dt = appointment.appointment_date
- procedure.start_time = appointment.appointment_time
- procedure.save()
- procedure.submit()
- return procedure
-
-def create_vital_signs(appointment):
- vital_signs = frappe.new_doc('Vital Signs')
- vital_signs.patient = appointment.patient
- vital_signs.signs_date = appointment.appointment_date
- vital_signs.signs_time = appointment.appointment_time
- vital_signs.temperature = 38.5
- vital_signs.save()
- vital_signs.submit()
- return vital_signs
-
-def create_lab_test_template(medical_department):
- if frappe.db.exists('Lab Test Template', 'Blood Test'):
- return frappe.get_doc('Lab Test Template', 'Blood Test')
-
- template = frappe.new_doc('Lab Test Template')
- template.lab_test_name = 'Blood Test'
- template.lab_test_code = 'Blood Test'
- template.lab_test_group = 'Services'
- template.department = medical_department
- template.is_billable = 1
- template.lab_test_rate = 2000
- template.save()
- return template
-
-def create_lab_test(template, patient):
- lab_test = frappe.new_doc('Lab Test')
- lab_test.patient = patient
- lab_test.patient_sex = frappe.db.get_value('Patient', patient, 'sex')
- lab_test.template = template
- lab_test.save()
- lab_test.submit()
- return lab_test
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_relation/__init__.py b/erpnext/healthcare/doctype/patient_relation/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/patient_relation/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/patient_relation/patient_relation.json b/erpnext/healthcare/doctype/patient_relation/patient_relation.json
deleted file mode 100644
index 376f7f7..0000000
--- a/erpnext/healthcare/doctype/patient_relation/patient_relation.json
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2017-04-26 15:40:11.561855",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "patient",
- "relation",
- "description"
- ],
- "fields": [
- {
- "fieldname": "relation",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Relation",
- "options": "\nFather\nMother\nSpouse\nSiblings\nFamily\nOther",
- "search_index": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fieldname": "description",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Description"
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-01-29 12:45:40.081899",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Relation",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_relation/patient_relation.py b/erpnext/healthcare/doctype/patient_relation/patient_relation.py
deleted file mode 100644
index 150b962..0000000
--- a/erpnext/healthcare/doctype/patient_relation/patient_relation.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class PatientRelation(Document):
- pass
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/__init__.py b/erpnext/healthcare/doctype/practitioner_schedule/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js
deleted file mode 100644
index 7cb7c4b..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.js
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Practitioner Schedule', {
- refresh: function(frm) {
- cur_frm.fields_dict["time_slots"].grid.wrapper.find('.grid-add-row').hide();
- cur_frm.fields_dict["time_slots"].grid.add_custom_button(__('Add Time Slots'), () => {
- let d = new frappe.ui.Dialog({
- fields: [
- {fieldname: 'days', label: __('Select Days'), fieldtype: 'MultiSelect',
- options:[
- {value:'Sunday', label:__('Sunday')},
- {value:'Monday', label:__('Monday')},
- {value:'Tuesday', label:__('Tuesday')},
- {value:'Wednesday', label:__('Wednesday')},
- {value:'Thursday', label:__('Thursday')},
- {value:'Friday', label:__('Friday')},
- {value:'Saturday', label:__('Saturday')},
- ], reqd: 1},
- {fieldname: 'from_time', label: __('From'), fieldtype: 'Time',
- 'default': '09:00:00', reqd: 1},
- {fieldname: 'to_time', label: __('To'), fieldtype: 'Time',
- 'default': '12:00:00', reqd: 1},
- {fieldname: 'duration', label: __('Appointment Duration (mins)'),
- fieldtype:'Int', 'default': 15, reqd: 1},
- ],
- primary_action_label: __('Add Timeslots'),
- primary_action: () => {
- let values = d.get_values();
- if (values) {
- let slot_added = false;
- values.days.split(',').forEach(function(day){
- day = $.trim(day);
- if (['Sunday', 'Monday', 'Tuesday', 'Wednesday',
- 'Thursday', 'Friday', 'Saturday'].includes(day)){
- add_slots(day);
- }
- });
-
- function check_overlap_or_add_slot(week_day, cur_time, end_time, add_slots_to_child){
- let overlap = false;
- while (cur_time < end_time) {
- let add_to_child = true;
- let to_time = cur_time.clone().add(values.duration, 'minutes');
- if (to_time <= end_time) {
- if (frm.doc.time_slots){
- frm.doc.time_slots.forEach(function(slot) {
- if (slot.day == week_day){
- let slot_from_moment = moment(slot.from_time, 'HH:mm:ss');
- let slot_to_moment = moment(slot.to_time, 'HH:mm:ss');
- if (cur_time.isSame(slot_from_moment) || cur_time.isBetween(slot_from_moment, slot_to_moment) ||
- to_time.isSame(slot_to_moment) || to_time.isBetween(slot_from_moment, slot_to_moment)) {
- overlap = true;
- if (add_slots_to_child) {
- frappe.show_alert({
- message:__('Time slot skiped, the slot {0} to {1} overlap exisiting slot {2} to {3}',
- [cur_time.format('HH:mm:ss'), to_time.format('HH:mm:ss'), slot.from_time, slot.to_time]),
- indicator:'orange'
- });
- add_to_child = false;
- }
- }
- }
- });
- }
- // add a new timeslot
- if (add_to_child && add_slots_to_child) {
- frm.add_child('time_slots', {
- from_time: cur_time.format('HH:mm:ss'),
- to_time: to_time.format('HH:mm:ss'),
- day: week_day
- });
- slot_added = true;
- }
- }
- cur_time = to_time;
- }
- return overlap;
- }
-
- function add_slots(week_day) {
- let cur_time = moment(values.from_time, 'HH:mm:ss');
- let end_time = moment(values.to_time, 'HH:mm:ss');
- if (check_overlap_or_add_slot(week_day, cur_time, end_time, false)) {
- frappe.confirm(__('Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?', [week_day]),
- function() {
- // if Yes
- check_overlap_or_add_slot(week_day, cur_time, end_time, true);
- },
- function() {
- // if No
- frappe.show_alert({
- message: __('Slots for {0} are not added to the schedule', [week_day]),
- indicator: 'red'
- });
- }
- );
- } else {
- check_overlap_or_add_slot(week_day, cur_time, end_time, true);
- }
- }
-
- frm.refresh_field('time_slots');
-
- if (slot_added) {
- frappe.show_alert({
- message: __('Time slots added'),
- indicator: 'green'
- });
- }
- }
- },
- });
- d.show();
- });
- }
-});
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json
deleted file mode 100644
index a21825e..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- "actions": [],
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:schedule_name",
- "beta": 1,
- "creation": "2017-05-03 17:28:03.926787",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "disabled",
- "schedule_details_section",
- "schedule_name",
- "time_slots"
- ],
- "fields": [
- {
- "fieldname": "schedule_name",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Schedule Name",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "time_slots",
- "fieldtype": "Table",
- "label": "Time Slots",
- "options": "Healthcare Schedule Time Slot"
- },
- {
- "default": "0",
- "fieldname": "disabled",
- "fieldtype": "Check",
- "label": "Disabled",
- "print_hide": 1
- },
- {
- "fieldname": "schedule_details_section",
- "fieldtype": "Section Break",
- "label": "Schedule Details"
- }
- ],
- "links": [],
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Practitioner Schedule",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "share": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.py b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.py
deleted file mode 100644
index 8bd0937..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class PractitionerSchedule(Document):
- def autoname(self):
- self.name = self.schedule_name
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.js b/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.js
deleted file mode 100644
index 32dac2c..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Practitioner Schedule", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Practitioner Schedule
- () => frappe.tests.make('Practitioner Schedule', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.py b/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.py
deleted file mode 100644
index 52638cb..0000000
--- a/erpnext/healthcare/doctype/practitioner_schedule/test_practitioner_schedule.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestPractitionerSchedule(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/practitioner_service_unit_schedule/__init__.py b/erpnext/healthcare/doctype/practitioner_service_unit_schedule/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/practitioner_service_unit_schedule/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/practitioner_service_unit_schedule/practitioner_service_unit_schedule.json b/erpnext/healthcare/doctype/practitioner_service_unit_schedule/practitioner_service_unit_schedule.json
deleted file mode 100644
index 4c283aa..0000000
--- a/erpnext/healthcare/doctype/practitioner_service_unit_schedule/practitioner_service_unit_schedule.json
+++ /dev/null
@@ -1,110 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2017-11-16 12:19:17.163786",
- "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": "schedule",
- "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": "Schedule",
- "length": 0,
- "no_copy": 0,
- "options": "Practitioner Schedule",
- "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": "service_unit",
- "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": "Service Unit",
- "length": 0,
- "no_copy": 0,
- "options": "Healthcare Service Unit",
- "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:33:07.936958",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Practitioner Service Unit Schedule",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "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/healthcare/doctype/practitioner_service_unit_schedule/practitioner_service_unit_schedule.py b/erpnext/healthcare/doctype/practitioner_service_unit_schedule/practitioner_service_unit_schedule.py
deleted file mode 100644
index c18a440..0000000
--- a/erpnext/healthcare/doctype/practitioner_service_unit_schedule/practitioner_service_unit_schedule.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class PractitionerServiceUnitSchedule(Document):
- pass
diff --git a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.js b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.js
deleted file mode 100644
index 94b444c..0000000
--- a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Prescription Dosage', {
-});
diff --git a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.json b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.json
deleted file mode 100644
index 9fb0dbc..0000000
--- a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.json
+++ /dev/null
@@ -1,145 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:dosage",
- "beta": 1,
- "creation": "2016-09-16 15:49:25.327610",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "dosage",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Dosage",
- "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": "dosage_strength",
- "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,
- "length": 0,
- "no_copy": 0,
- "options": "Dosage Strength",
- "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-10-05 11:20:47.558464",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Prescription Dosage",
- "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": "Healthcare Administrator",
- "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": "Physician",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "dosage",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.py b/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.py
deleted file mode 100644
index dea263d..0000000
--- a/erpnext/healthcare/doctype/prescription_dosage/prescription_dosage.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class PrescriptionDosage(Document):
- pass
diff --git a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js
deleted file mode 100644
index 009614f..0000000
--- a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Prescription Dosage", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Prescription Dosage
- () => frappe.tests.make('Prescription Dosage', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.py b/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.py
deleted file mode 100644
index e61a418..0000000
--- a/erpnext/healthcare/doctype/prescription_dosage/test_prescription_dosage.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestPrescriptionDosage(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/prescription_duration/__init__.py b/erpnext/healthcare/doctype/prescription_duration/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.js b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.js
deleted file mode 100644
index dd5887c..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Prescription Duration', {
-});
diff --git a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.json b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.json
deleted file mode 100644
index c4f6c5f..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.json
+++ /dev/null
@@ -1,145 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "",
- "beta": 1,
- "creation": "2016-09-16 15:50:28.895789",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "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": "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": 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": "period",
- "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": "Period",
- "length": 0,
- "no_copy": 0,
- "options": "Hour\nDay\nWeek\nMonth",
- "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-08-31 13:42:51.325725",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Prescription Duration",
- "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": "Healthcare Administrator",
- "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": "Physician",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "",
- "show_name_in_global_search": 0,
- "sort_field": "",
- "sort_order": "ASC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.py b/erpnext/healthcare/doctype/prescription_duration/prescription_duration.py
deleted file mode 100644
index 96ddf8d..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/prescription_duration.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-from frappe.utils import cstr
-
-class PrescriptionDuration(Document):
- def autoname(self):
- self.name = " ".join(filter(None,
- [cstr(self.get(f)).strip() for f in ["number", "period"]]))
- def get_days(self):
- days = 0
- duration = self
- if(duration.period == 'Day'):
- days = duration.number
- if(duration.period == 'Hour'):
- days = (duration.number)/24
- if(duration.period == 'Week'):
- days = (duration.number*7)
- if(duration.period == 'Month'):
- days = (duration.number*30)
- return days
- def get_weeks(self):
- weeks = 0
- duration = self
- if(duration.period == 'Day'):
- weeks = (duration.number)/7
- #if(duration.period == 'Hour'):
- # weeks = (duration.number)/x
- if(duration.period == 'Week'):
- weeks = duration.number
- if(duration.period == 'Month'):
- weeks = duration.number*4
- return weeks
- def get_months(self):
- months = 0
- duration = self
- if(duration.period == 'Day'):
- months = (duration.number)/30
- #if(duration.period == 'Hour'):
- # months = (duration.number)/x
- if(duration.period == 'Week'):
- months = (duration.number)/4
- if(duration.period == 'Month'):
- months = duration.number
- return months
- def get_hours(self):
- hours = 0
- duration = self
- if(duration.period == 'Day'):
- hours = (duration.number*24)
- if(duration.period == 'Hour'):
- hours = duration.number
- if(duration.period == 'Week'):
- hours = (duration.number*24)*7
- if(duration.period == 'Month'):
- hours = (duration.number*24)*30
- return hours
- def get_minutes(self):
- minutes = 0
- duration = self
- if(duration.period == 'Day'):
- minutes = (duration.number*1440)
- if(duration.period == 'Hour'):
- minutes = (duration.number*60)
- if(duration.period == 'Week'):
- minutes = (duration.number*10080)
- if(duration.period == 'Month'):
- minutes = (duration.number*43800)
- return minutes
diff --git a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js
deleted file mode 100644
index 4971e79..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Prescription Duration", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Prescription Duration
- () => frappe.tests.make('Prescription Duration', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.py b/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.py
deleted file mode 100644
index fe5524c..0000000
--- a/erpnext/healthcare/doctype/prescription_duration/test_prescription_duration.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestPrescriptionDuration(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/procedure_prescription/__init__.py b/erpnext/healthcare/doctype/procedure_prescription/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/procedure_prescription/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json
deleted file mode 100644
index e4c01d7..0000000
--- a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.json
+++ /dev/null
@@ -1,99 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "beta": 1,
- "creation": "2017-11-17 15:52:48.324157",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "procedure",
- "procedure_name",
- "department",
- "practitioner",
- "date",
- "comments",
- "appointment_booked",
- "procedure_created",
- "invoiced"
- ],
- "fields": [
- {
- "fieldname": "procedure",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Clinical Procedure",
- "options": "Clinical Procedure Template",
- "reqd": 1
- },
- {
- "fetch_from": "procedure.template",
- "fieldname": "procedure_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Procedure Name"
- },
- {
- "fetch_from": "procedure.medical_department",
- "fieldname": "department",
- "fieldtype": "Link",
- "label": "Department",
- "options": "Medical Department"
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Referring Practitioner",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Date"
- },
- {
- "fieldname": "comments",
- "fieldtype": "Data",
- "label": "Comments"
- },
- {
- "default": "0",
- "fieldname": "appointment_booked",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Appointment Booked",
- "search_index": 1
- },
- {
- "default": "0",
- "fieldname": "procedure_created",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Procedure Created",
- "no_copy": 1,
- "search_index": 1
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "read_only": 1,
- "search_index": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-02-26 15:42:33.988081",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Procedure Prescription",
- "owner": "Administrator",
- "permissions": [],
- "restrict_to_domain": "Healthcare",
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.py b/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.py
deleted file mode 100644
index 62ea9f1..0000000
--- a/erpnext/healthcare/doctype/procedure_prescription/procedure_prescription.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class ProcedurePrescription(Document):
- pass
diff --git a/erpnext/healthcare/doctype/sample_collection/__init__.py b/erpnext/healthcare/doctype/sample_collection/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/sample_collection/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.js b/erpnext/healthcare/doctype/sample_collection/sample_collection.js
deleted file mode 100644
index ddf8285..0000000
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2016, ESS and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Sample Collection', {
- refresh: function(frm) {
- if (frappe.defaults.get_default('create_sample_collection_for_lab_test')) {
- frm.add_custom_button(__('View Lab Tests'), function() {
- frappe.route_options = {'sample': frm.doc.name};
- frappe.set_route('List', 'Lab Test');
- });
- }
- }
-});
-
-frappe.ui.form.on('Sample Collection', 'patient', function(frm) {
- if(frm.doc.patient){
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
- args: {
- patient: frm.doc.patient
- },
- callback: function (data) {
- var age = null;
- if (data.message.dob){
- age = calculate_age(data.message.dob);
- }
- frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
- frappe.model.set_value(frm.doctype,frm.docname, 'patient_sex', data.message.sex);
- }
- });
- }
-});
-
-var calculate_age = function(birth) {
- var ageMS = Date.parse(Date()) - Date.parse(birth);
- var age = new Date();
- age.setTime(ageMS);
- var years = age.getFullYear() - 1970;
- return `${years} ${__('Years(s)')} ${age.getMonth()} ${__('Month(s)')} ${age.getDate()} ${__('Day(s)')}`;
-};
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
deleted file mode 100644
index 83383e3..0000000
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json
+++ /dev/null
@@ -1,256 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2016-04-05 15:58:18.076977",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
- "patient_details_section",
- "naming_series",
- "patient",
- "patient_name",
- "patient_age",
- "patient_sex",
- "column_break_4",
- "inpatient_record",
- "company",
- "invoiced",
- "section_break_6",
- "sample",
- "sample_uom",
- "sample_qty",
- "column_break_10",
- "collected_by",
- "collected_time",
- "num_print",
- "section_break_15",
- "sample_details",
- "amended_from"
- ],
- "fields": [
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "bold": 1,
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Series",
- "no_copy": 1,
- "options": "HLC-SC-.YYYY.-",
- "print_hide": 1,
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Invoiced",
- "no_copy": 1,
- "read_only": 1,
- "search_index": 1
- },
- {
- "fetch_from": "inpatient_record.patient",
- "fieldname": "patient",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break",
- "hide_days": 1,
- "hide_seconds": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Age",
- "read_only": 1
- },
- {
- "fetch_from": "patient.sex",
- "fieldname": "patient_sex",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Gender",
- "options": "Gender",
- "read_only": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company"
- },
- {
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Sample Details"
- },
- {
- "fieldname": "sample",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Sample",
- "options": "Lab Test Sample",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fetch_from": "sample.sample_uom",
- "fieldname": "sample_uom",
- "fieldtype": "Data",
- "hide_days": 1,
- "hide_seconds": 1,
- "in_list_view": 1,
- "label": "UOM",
- "read_only": 1
- },
- {
- "fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hide_days": 1,
- "hide_seconds": 1
- },
- {
- "fieldname": "collected_by",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "ignore_user_permissions": 1,
- "label": "Collected By",
- "options": "User"
- },
- {
- "fieldname": "collected_time",
- "fieldtype": "Datetime",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Collected On"
- },
- {
- "allow_on_submit": 1,
- "default": "1",
- "description": "Number of prints required for labelling the samples",
- "fieldname": "num_print",
- "fieldtype": "Int",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "No. of prints",
- "print_hide": 1,
- "report_hide": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "hide_days": 1,
- "hide_seconds": 1,
- "label": "Amended From",
- "no_copy": 1,
- "options": "Sample Collection",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "section_break_15",
- "fieldtype": "Section Break",
- "hide_days": 1,
- "hide_seconds": 1
- },
- {
- "default": "0",
- "fieldname": "sample_qty",
- "fieldtype": "Float",
- "hide_days": 1,
- "hide_seconds": 1,
- "in_list_view": 1,
- "label": "Quantity"
- },
- {
- "fieldname": "sample_details",
- "fieldtype": "Long Text",
- "hide_days": 1,
- "hide_seconds": 1,
- "ignore_xss_filter": 1,
- "label": "Collection Details"
- },
- {
- "fieldname": "patient_details_section",
- "fieldtype": "Section Break",
- "label": "Patient Details"
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-07-30 16:53:13.076104",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Sample Collection",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Laboratory User",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient, sample",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.py b/erpnext/healthcare/doctype/sample_collection/sample_collection.py
deleted file mode 100644
index 461f809..0000000
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe.utils import flt
-from frappe import _
-
-class SampleCollection(Document):
- def validate(self):
- if flt(self.sample_qty) <= 0:
- frappe.throw(_('Sample Quantity cannot be negative or 0'), title=_('Invalid Quantity'))
diff --git a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js
deleted file mode 100644
index 2b4aed7..0000000
--- a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sample Collection", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Sample Collection
- () => frappe.tests.make('Sample Collection', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.py b/erpnext/healthcare/doctype/sample_collection/test_sample_collection.py
deleted file mode 100644
index 0b16173..0000000
--- a/erpnext/healthcare/doctype/sample_collection/test_sample_collection.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-# test_records = frappe.get_test_records('Sample Collection')
-
-class TestSampleCollection(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/sensitivity/__init__.py b/erpnext/healthcare/doctype/sensitivity/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/sensitivity/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/sensitivity/sensitivity.js b/erpnext/healthcare/doctype/sensitivity/sensitivity.js
deleted file mode 100644
index f9c9002..0000000
--- a/erpnext/healthcare/doctype/sensitivity/sensitivity.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Sensitivity', {
-});
diff --git a/erpnext/healthcare/doctype/sensitivity/sensitivity.json b/erpnext/healthcare/doctype/sensitivity/sensitivity.json
deleted file mode 100644
index eddfda9..0000000
--- a/erpnext/healthcare/doctype/sensitivity/sensitivity.json
+++ /dev/null
@@ -1,115 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:sensitivity",
- "beta": 1,
- "creation": "2016-02-23 11:12:54.623249",
- "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": "sensitivity",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 1,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Sensitivity",
- "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-10-05 11:19:12.110308",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Sensitivity",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 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": "Laboratory User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
- },
- {
- "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": "Healthcare Administrator",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "sensitivity",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "sensitivity",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/sensitivity/sensitivity.py b/erpnext/healthcare/doctype/sensitivity/sensitivity.py
deleted file mode 100644
index bf7c36b..0000000
--- a/erpnext/healthcare/doctype/sensitivity/sensitivity.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class Sensitivity(Document):
- pass
diff --git a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js
deleted file mode 100644
index c2cf406..0000000
--- a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sensitivity", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Sensitivity
- () => frappe.tests.make('Sensitivity', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.py b/erpnext/healthcare/doctype/sensitivity/test_sensitivity.py
deleted file mode 100644
index 1616d2d..0000000
--- a/erpnext/healthcare/doctype/sensitivity/test_sensitivity.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-# test_records = frappe.get_test_records('Sensitivity')
-
-class TestSensitivity(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/sensitivity_test_result/__init__.py b/erpnext/healthcare/doctype/sensitivity_test_result/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/sensitivity_test_result/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/sensitivity_test_result/sensitivity_test_result.json b/erpnext/healthcare/doctype/sensitivity_test_result/sensitivity_test_result.json
deleted file mode 100644
index 768c177..0000000
--- a/erpnext/healthcare/doctype/sensitivity_test_result/sensitivity_test_result.json
+++ /dev/null
@@ -1,103 +0,0 @@
-{
- "allow_copy": 1,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 1,
- "creation": "2016-02-22 15:18:01.769903",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "antibiotic",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 1,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Antibiotic",
- "length": 0,
- "no_copy": 0,
- "options": "Antibiotic",
- "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": "antibiotic_sensitivity",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 1,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Sensitivity",
- "length": 0,
- "no_copy": 0,
- "options": "Sensitivity",
- "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-10-05 11:08:06.327972",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Sensitivity Test Result",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "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/healthcare/doctype/sensitivity_test_result/sensitivity_test_result.py b/erpnext/healthcare/doctype/sensitivity_test_result/sensitivity_test_result.py
deleted file mode 100644
index 64f1e6c..0000000
--- a/erpnext/healthcare/doctype/sensitivity_test_result/sensitivity_test_result.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-from frappe.model.document import Document
-
-class SensitivityTestResult(Document):
- pass
diff --git a/erpnext/healthcare/doctype/therapy_plan/__init__.py b/erpnext/healthcare/doctype/therapy_plan/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
deleted file mode 100644
index 113fa51..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-from frappe.utils import getdate, flt, nowdate
-from erpnext.healthcare.doctype.therapy_type.test_therapy_type import create_therapy_type
-from erpnext.healthcare.doctype.therapy_plan.therapy_plan import make_therapy_session, make_sales_invoice
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_patient, create_appointment
-
-class TestTherapyPlan(unittest.TestCase):
- def test_creation_on_encounter_submission(self):
- patient, medical_department, practitioner = create_healthcare_docs()
- encounter = create_encounter(patient, medical_department, practitioner)
- self.assertTrue(frappe.db.exists('Therapy Plan', encounter.therapy_plan))
-
- def test_status(self):
- plan = create_therapy_plan()
- self.assertEqual(plan.status, 'Not Started')
-
- session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
- frappe.get_doc(session).submit()
- self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'In Progress')
-
- session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
- frappe.get_doc(session).submit()
- self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
-
- patient, medical_department, practitioner = create_healthcare_docs()
- appointment = create_appointment(patient, practitioner, nowdate())
- session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
- session = frappe.get_doc(session)
- session.submit()
- self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
- session.cancel()
- self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
-
- def test_therapy_plan_from_template(self):
- patient = create_patient()
- template = create_therapy_plan_template()
- # check linked item
- self.assertTrue(frappe.db.exists('Therapy Plan Template', {'linked_item': 'Complete Rehab'}))
-
- plan = create_therapy_plan(template)
- # invoice
- si = make_sales_invoice(plan.name, patient, '_Test Company', template)
- si.save()
-
- therapy_plan_template_amt = frappe.db.get_value('Therapy Plan Template', template, 'total_amount')
- self.assertEqual(si.items[0].amount, therapy_plan_template_amt)
-
-
-def create_therapy_plan(template=None):
- patient = create_patient()
- therapy_type = create_therapy_type()
- plan = frappe.new_doc('Therapy Plan')
- plan.patient = patient
- plan.start_date = getdate()
-
- if template:
- plan.therapy_plan_template = template
- plan = plan.set_therapy_details_from_template()
- else:
- plan.append('therapy_plan_details', {
- 'therapy_type': therapy_type.name,
- 'no_of_sessions': 2
- })
-
- plan.save()
- return plan
-
-def create_encounter(patient, medical_department, practitioner):
- encounter = frappe.new_doc('Patient Encounter')
- encounter.patient = patient
- encounter.practitioner = practitioner
- encounter.medical_department = medical_department
- therapy_type = create_therapy_type()
- encounter.append('therapies', {
- 'therapy_type': therapy_type.name,
- 'no_of_sessions': 2
- })
- encounter.save()
- encounter.submit()
- return encounter
-
-def create_therapy_plan_template():
- template_name = frappe.db.exists('Therapy Plan Template', 'Complete Rehab')
- if not template_name:
- therapy_type = create_therapy_type()
- template = frappe.new_doc('Therapy Plan Template')
- template.plan_name = template.item_code = template.item_name = 'Complete Rehab'
- template.item_group = 'Services'
- rate = frappe.db.get_value('Therapy Type', therapy_type.name, 'rate')
- template.append('therapy_types', {
- 'therapy_type': therapy_type.name,
- 'no_of_sessions': 2,
- 'rate': rate,
- 'amount': 2 * flt(rate)
- })
- template.save()
- template_name = template.name
-
- return template_name
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js
deleted file mode 100644
index 42e231d..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Therapy Plan', {
- setup: function(frm) {
- frm.get_field('therapy_plan_details').grid.editable_fields = [
- {fieldname: 'therapy_type', columns: 6},
- {fieldname: 'no_of_sessions', columns: 2},
- {fieldname: 'sessions_completed', columns: 2}
- ];
- },
-
- refresh: function(frm) {
- if (!frm.doc.__islocal) {
- frm.trigger('show_progress_for_therapies');
- if (frm.doc.status != 'Completed') {
- let therapy_types = (frm.doc.therapy_plan_details || []).map(function(d){ return d.therapy_type; });
- const fields = [{
- fieldtype: 'Link',
- label: __('Therapy Type'),
- fieldname: 'therapy_type',
- options: 'Therapy Type',
- reqd: 1,
- get_query: function() {
- return {
- filters: { 'therapy_type': ['in', therapy_types]}
- };
- }
- }];
-
- frm.add_custom_button(__('Therapy Session'), function() {
- frappe.prompt(fields, data => {
- frappe.call({
- method: 'erpnext.healthcare.doctype.therapy_plan.therapy_plan.make_therapy_session',
- args: {
- therapy_plan: frm.doc.name,
- patient: frm.doc.patient,
- therapy_type: data.therapy_type,
- company: frm.doc.company
- },
- freeze: true,
- callback: function(r) {
- if (r.message) {
- frappe.model.sync(r.message);
- frappe.set_route('Form', r.message.doctype, r.message.name);
- }
- }
- });
- }, __('Select Therapy Type'), __('Create'));
- }, __('Create'));
- }
-
- if (frm.doc.therapy_plan_template && !frm.doc.invoiced) {
- frm.add_custom_button(__('Sales Invoice'), function() {
- frm.trigger('make_sales_invoice');
- }, __('Create'));
- }
- }
-
- if (frm.doc.therapy_plan_template) {
- frm.fields_dict.therapy_plan_details.grid.update_docfield_property(
- 'therapy_type', 'read_only', 1
- );
- frm.fields_dict.therapy_plan_details.grid.update_docfield_property(
- 'no_of_sessions', 'read_only', 1
- );
- }
- },
-
- make_sales_invoice: function(frm) {
- frappe.call({
- args: {
- 'reference_name': frm.doc.name,
- 'patient': frm.doc.patient,
- 'company': frm.doc.company,
- 'therapy_plan_template': frm.doc.therapy_plan_template
- },
- method: 'erpnext.healthcare.doctype.therapy_plan.therapy_plan.make_sales_invoice',
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
- }
- });
- },
-
- therapy_plan_template: function(frm) {
- if (frm.doc.therapy_plan_template) {
- frappe.call({
- method: 'set_therapy_details_from_template',
- doc: frm.doc,
- freeze: true,
- freeze_message: __('Fetching Template Details'),
- callback: function() {
- refresh_field('therapy_plan_details');
- }
- });
- }
- },
-
- show_progress_for_therapies: function(frm) {
- let bars = [];
- let message = '';
-
- // completed sessions
- let title = __('{0} sessions completed', [frm.doc.total_sessions_completed]);
- if (frm.doc.total_sessions_completed === 1) {
- title = __('{0} session completed', [frm.doc.total_sessions_completed]);
- }
- title += __(' out of {0}', [frm.doc.total_sessions]);
-
- bars.push({
- 'title': title,
- 'width': (frm.doc.total_sessions_completed / frm.doc.total_sessions * 100) + '%',
- 'progress_class': 'progress-bar-success'
- });
- if (bars[0].width == '0%') {
- bars[0].width = '0.5%';
- }
- message = title;
- frm.dashboard.add_progress(__('Status'), bars, message);
- },
-});
-
-frappe.ui.form.on('Therapy Plan Detail', {
- no_of_sessions: function(frm) {
- let total = 0;
- $.each(frm.doc.therapy_plan_details, function(_i, e) {
- total += e.no_of_sessions;
- });
- frm.set_value('total_sessions', total);
- refresh_field('total_sessions');
- }
-});
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json
deleted file mode 100644
index c03e9de..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json
+++ /dev/null
@@ -1,179 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2020-03-29 20:56:49.758602",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "patient",
- "patient_name",
- "invoiced",
- "column_break_4",
- "company",
- "status",
- "start_date",
- "section_break_3",
- "therapy_plan_template",
- "therapy_plan_details",
- "title",
- "section_break_9",
- "total_sessions",
- "column_break_11",
- "total_sessions_completed"
- ],
- "fields": [
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fieldname": "start_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Start Date",
- "reqd": 1
- },
- {
- "fieldname": "section_break_3",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "therapy_plan_details",
- "fieldtype": "Table",
- "label": "Therapy Plan Details",
- "options": "Therapy Plan Detail",
- "read_only_depends_on": "therapy_plan_template",
- "reqd": 1
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Naming Series",
- "options": "HLC-THP-.YYYY.-"
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "default": "{patient_name}",
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "no_copy": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_9",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "total_sessions",
- "fieldtype": "Int",
- "label": "Total Sessions",
- "read_only": 1
- },
- {
- "fieldname": "column_break_11",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "total_sessions_completed",
- "fieldtype": "Int",
- "label": "Total Sessions Completed",
- "read_only": 1
- },
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "label": "Status",
- "options": "Not Started\nIn Progress\nCompleted\nCancelled",
- "read_only": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Company",
- "options": "Company",
- "reqd": 1
- },
- {
- "fieldname": "therapy_plan_template",
- "fieldtype": "Link",
- "label": "Therapy Plan Template",
- "options": "Therapy Plan Template",
- "set_only_once": 1
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- }
- ],
- "links": [],
- "modified": "2020-11-04 18:13:13.564999",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Therapy Plan",
- "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": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "search_fields": "patient",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
deleted file mode 100644
index e209660..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe.utils import flt, today
-
-class TherapyPlan(Document):
- def validate(self):
- self.set_totals()
- self.set_status()
-
- def set_status(self):
- if not self.total_sessions_completed:
- self.status = 'Not Started'
- else:
- if self.total_sessions_completed < self.total_sessions:
- self.status = 'In Progress'
- elif self.total_sessions_completed == self.total_sessions:
- self.status = 'Completed'
-
- def set_totals(self):
- total_sessions = 0
- total_sessions_completed = 0
- for entry in self.therapy_plan_details:
- if entry.no_of_sessions:
- total_sessions += entry.no_of_sessions
- if entry.sessions_completed:
- total_sessions_completed += entry.sessions_completed
-
- self.db_set('total_sessions', total_sessions)
- self.db_set('total_sessions_completed', total_sessions_completed)
-
- @frappe.whitelist()
- def set_therapy_details_from_template(self):
- # Add therapy types in the child table
- self.set('therapy_plan_details', [])
- therapy_plan_template = frappe.get_doc('Therapy Plan Template', self.therapy_plan_template)
-
- for data in therapy_plan_template.therapy_types:
- self.append('therapy_plan_details', {
- 'therapy_type': data.therapy_type,
- 'no_of_sessions': data.no_of_sessions
- })
- return self
-
-
-@frappe.whitelist()
-def make_therapy_session(therapy_plan, patient, therapy_type, company, appointment=None):
- therapy_type = frappe.get_doc('Therapy Type', therapy_type)
-
- therapy_session = frappe.new_doc('Therapy Session')
- therapy_session.therapy_plan = therapy_plan
- therapy_session.company = company
- therapy_session.patient = patient
- therapy_session.therapy_type = therapy_type.name
- therapy_session.duration = therapy_type.default_duration
- therapy_session.rate = therapy_type.rate
- therapy_session.exercises = therapy_type.exercises
- therapy_session.appointment = appointment
-
- if frappe.flags.in_test:
- therapy_session.start_date = today()
- return therapy_session.as_dict()
-
-
-@frappe.whitelist()
-def make_sales_invoice(reference_name, patient, company, therapy_plan_template):
- from erpnext.stock.get_item_details import get_item_details
- si = frappe.new_doc('Sales Invoice')
- si.company = company
- si.patient = patient
- si.customer = frappe.db.get_value('Patient', patient, 'customer')
-
- item = frappe.db.get_value('Therapy Plan Template', therapy_plan_template, 'linked_item')
- price_list, price_list_currency = frappe.db.get_values('Price List', {'selling': 1}, ['name', 'currency'])[0]
- args = {
- 'doctype': 'Sales Invoice',
- 'item_code': item,
- 'company': company,
- 'customer': si.customer,
- 'selling_price_list': price_list,
- 'price_list_currency': price_list_currency,
- 'plc_conversion_rate': 1.0,
- 'conversion_rate': 1.0
- }
-
- item_line = si.append('items', {})
- item_details = get_item_details(args)
- item_line.item_code = item
- item_line.qty = 1
- item_line.rate = item_details.price_list_rate
- item_line.amount = flt(item_line.rate) * flt(item_line.qty)
- item_line.reference_dt = 'Therapy Plan'
- item_line.reference_dn = reference_name
- item_line.description = item_details.description
-
- si.set_missing_values(for_validate = True)
- return si
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan_dashboard.py b/erpnext/healthcare/doctype/therapy_plan/therapy_plan_dashboard.py
deleted file mode 100644
index 6526acd..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan_dashboard.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'therapy_plan',
- 'non_standard_fieldnames': {
- 'Sales Invoice': 'reference_dn'
- },
- 'transactions': [
- {
- 'label': _('Therapy Sessions'),
- 'items': ['Therapy Session']
- },
- {
- 'label': _('Billing'),
- 'items': ['Sales Invoice']
- }
- ],
- 'disable_create_buttons': ['Sales Invoice']
- }
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan_list.js b/erpnext/healthcare/doctype/therapy_plan/therapy_plan_list.js
deleted file mode 100644
index 63967af..0000000
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan_list.js
+++ /dev/null
@@ -1,11 +0,0 @@
-frappe.listview_settings['Therapy Plan'] = {
- get_indicator: function(doc) {
- var colors = {
- 'Completed': 'green',
- 'In Progress': 'orange',
- 'Not Started': 'red',
- 'Cancelled': 'grey'
- };
- return [__(doc.status), colors[doc.status], 'status,=,' + doc.status];
- }
-};
diff --git a/erpnext/healthcare/doctype/therapy_plan_detail/__init__.py b/erpnext/healthcare/doctype/therapy_plan_detail/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_detail/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/therapy_plan_detail/therapy_plan_detail.json b/erpnext/healthcare/doctype/therapy_plan_detail/therapy_plan_detail.json
deleted file mode 100644
index 77f08af..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_detail/therapy_plan_detail.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "actions": [],
- "creation": "2020-03-29 20:52:57.068731",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "therapy_type",
- "no_of_sessions",
- "sessions_completed"
- ],
- "fields": [
- {
- "fieldname": "therapy_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Therapy Type",
- "options": "Therapy Type",
- "reqd": 1
- },
- {
- "fieldname": "no_of_sessions",
- "fieldtype": "Int",
- "in_list_view": 1,
- "label": "No of Sessions"
- },
- {
- "default": "0",
- "depends_on": "eval:doc.parenttype=='Therapy Plan';",
- "fieldname": "sessions_completed",
- "fieldtype": "Int",
- "label": "Sessions Completed",
- "no_copy": 1,
- "read_only": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-11-04 18:15:52.173450",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Therapy Plan Detail",
- "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/healthcare/doctype/therapy_plan_detail/therapy_plan_detail.py b/erpnext/healthcare/doctype/therapy_plan_detail/therapy_plan_detail.py
deleted file mode 100644
index 44211f3..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_detail/therapy_plan_detail.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class TherapyPlanDetail(Document):
- pass
diff --git a/erpnext/healthcare/doctype/therapy_plan_template/__init__.py b/erpnext/healthcare/doctype/therapy_plan_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/therapy_plan_template/test_therapy_plan_template.py b/erpnext/healthcare/doctype/therapy_plan_template/test_therapy_plan_template.py
deleted file mode 100644
index 33ee29d..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template/test_therapy_plan_template.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestTherapyPlanTemplate(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.js b/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.js
deleted file mode 100644
index 86de192..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Therapy Plan Template', {
- refresh: function(frm) {
- frm.set_query('therapy_type', 'therapy_types', () => {
- return {
- filters: {
- 'is_billable': 1
- }
- };
- });
- },
-
- set_totals: function(frm) {
- let total_sessions = 0;
- let total_amount = 0.0;
- frm.doc.therapy_types.forEach((d) => {
- if (d.no_of_sessions) total_sessions += cint(d.no_of_sessions);
- if (d.amount) total_amount += flt(d.amount);
- });
- frm.set_value('total_sessions', total_sessions);
- frm.set_value('total_amount', total_amount);
- frm.refresh_fields();
- }
-});
-
-frappe.ui.form.on('Therapy Plan Template Detail', {
- therapy_type: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- frappe.call('frappe.client.get', {
- doctype: 'Therapy Type',
- name: row.therapy_type
- }).then((res) => {
- row.rate = res.message.rate;
- if (!row.no_of_sessions)
- row.no_of_sessions = 1;
- row.amount = flt(row.rate) * cint(row.no_of_sessions);
- frm.refresh_field('therapy_types');
- frm.trigger('set_totals');
- });
- },
-
- no_of_sessions: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- row.amount = flt(row.rate) * cint(row.no_of_sessions);
- frm.refresh_field('therapy_types');
- frm.trigger('set_totals');
- },
-
- rate: function(frm, cdt, cdn) {
- let row = locals[cdt][cdn];
- row.amount = flt(row.rate) * cint(row.no_of_sessions);
- frm.refresh_field('therapy_types');
- frm.trigger('set_totals');
- }
-});
diff --git a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.json b/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.json
deleted file mode 100644
index 48fc896..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.json
+++ /dev/null
@@ -1,132 +0,0 @@
-{
- "actions": [],
- "autoname": "field:plan_name",
- "creation": "2020-09-22 17:51:38.861055",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "plan_name",
- "linked_item_details_section",
- "item_code",
- "item_name",
- "item_group",
- "column_break_6",
- "description",
- "linked_item",
- "therapy_types_section",
- "therapy_types",
- "section_break_11",
- "total_sessions",
- "column_break_13",
- "total_amount"
- ],
- "fields": [
- {
- "fieldname": "plan_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Plan Name",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "therapy_types_section",
- "fieldtype": "Section Break",
- "label": "Therapy Types"
- },
- {
- "fieldname": "therapy_types",
- "fieldtype": "Table",
- "label": "Therapy Types",
- "options": "Therapy Plan Template Detail",
- "reqd": 1
- },
- {
- "fieldname": "linked_item",
- "fieldtype": "Link",
- "label": "Linked Item",
- "options": "Item",
- "read_only": 1
- },
- {
- "fieldname": "linked_item_details_section",
- "fieldtype": "Section Break",
- "label": "Linked Item Details"
- },
- {
- "fieldname": "item_code",
- "fieldtype": "Data",
- "label": "Item Code",
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "item_name",
- "fieldtype": "Data",
- "label": "Item Name",
- "reqd": 1
- },
- {
- "fieldname": "item_group",
- "fieldtype": "Link",
- "label": "Item Group",
- "options": "Item Group",
- "reqd": 1
- },
- {
- "fieldname": "column_break_6",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "description",
- "fieldtype": "Small Text",
- "label": "Item Description"
- },
- {
- "fieldname": "total_amount",
- "fieldtype": "Currency",
- "label": "Total Amount",
- "read_only": 1
- },
- {
- "fieldname": "section_break_11",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "total_sessions",
- "fieldtype": "Int",
- "label": "Total Sessions",
- "read_only": 1
- },
- {
- "fieldname": "column_break_13",
- "fieldtype": "Column Break"
- }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2020-10-08 00:56:58.062105",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Therapy Plan Template",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py b/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py
deleted file mode 100644
index 748c12c..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe.utils import cint, flt
-from erpnext.healthcare.doctype.therapy_type.therapy_type import make_item_price
-
-class TherapyPlanTemplate(Document):
- def after_insert(self):
- self.create_item_from_template()
-
- def validate(self):
- self.set_totals()
-
- def on_update(self):
- doc_before_save = self.get_doc_before_save()
- if not doc_before_save: return
- if doc_before_save.item_name != self.item_name or doc_before_save.item_group != self.item_group \
- or doc_before_save.description != self.description:
- self.update_item()
-
- if doc_before_save.therapy_types != self.therapy_types:
- self.update_item_price()
-
- def set_totals(self):
- total_sessions = 0
- total_amount = 0
-
- for entry in self.therapy_types:
- total_sessions += cint(entry.no_of_sessions)
- total_amount += flt(entry.amount)
-
- self.total_sessions = total_sessions
- self.total_amount = total_amount
-
- def create_item_from_template(self):
- uom = frappe.db.exists('UOM', 'Nos') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
-
- item = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': self.item_code,
- 'item_name': self.item_name,
- 'item_group': self.item_group,
- 'description': self.description,
- 'is_sales_item': 1,
- 'is_service_item': 1,
- 'is_purchase_item': 0,
- 'is_stock_item': 0,
- 'show_in_website': 0,
- 'is_pro_applicable': 0,
- 'stock_uom': uom
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- make_item_price(item.name, self.total_amount)
- self.db_set('linked_item', item.name)
-
- def update_item(self):
- item_doc = frappe.get_doc('Item', {'item_code': self.linked_item})
- item_doc.item_name = self.item_name
- item_doc.item_group = self.item_group
- item_doc.description = self.description
- item_doc.ignore_mandatory = True
- item_doc.save(ignore_permissions=True)
-
- def update_item_price(self):
- item_price = frappe.get_doc('Item Price', {'item_code': self.linked_item})
- item_price.item_name = self.item_name
- item_price.price_list_rate = self.total_amount
- item_price.ignore_mandatory = True
- item_price.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template_dashboard.py b/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template_dashboard.py
deleted file mode 100644
index c748fbf..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'therapy_plan_template',
- 'transactions': [
- {
- 'label': _('Therapy Plans'),
- 'items': ['Therapy Plan']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/therapy_plan_template_detail/__init__.py b/erpnext/healthcare/doctype/therapy_plan_template_detail/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template_detail/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/therapy_plan_template_detail/therapy_plan_template_detail.json b/erpnext/healthcare/doctype/therapy_plan_template_detail/therapy_plan_template_detail.json
deleted file mode 100644
index 5553a11..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template_detail/therapy_plan_template_detail.json
+++ /dev/null
@@ -1,54 +0,0 @@
-{
- "actions": [],
- "creation": "2020-10-07 23:04:44.373381",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "therapy_type",
- "no_of_sessions",
- "rate",
- "amount"
- ],
- "fields": [
- {
- "fieldname": "therapy_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Therapy Type",
- "options": "Therapy Type",
- "reqd": 1
- },
- {
- "fieldname": "no_of_sessions",
- "fieldtype": "Int",
- "in_list_view": 1,
- "label": "No of Sessions"
- },
- {
- "fieldname": "rate",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Rate"
- },
- {
- "fieldname": "amount",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Amount",
- "read_only": 1
- }
- ],
- "istable": 1,
- "links": [],
- "modified": "2020-10-07 23:46:54.296322",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Therapy Plan Template Detail",
- "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/healthcare/doctype/therapy_plan_template_detail/therapy_plan_template_detail.py b/erpnext/healthcare/doctype/therapy_plan_template_detail/therapy_plan_template_detail.py
deleted file mode 100644
index 7b979fe..0000000
--- a/erpnext/healthcare/doctype/therapy_plan_template_detail/therapy_plan_template_detail.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class TherapyPlanTemplateDetail(Document):
- pass
diff --git a/erpnext/healthcare/doctype/therapy_session/__init__.py b/erpnext/healthcare/doctype/therapy_session/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/therapy_session/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/therapy_session/test_therapy_session.py b/erpnext/healthcare/doctype/therapy_session/test_therapy_session.py
deleted file mode 100644
index 75bb8df..0000000
--- a/erpnext/healthcare/doctype/therapy_session/test_therapy_session.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestTherapySession(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.js b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
deleted file mode 100644
index fd20003..0000000
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.js
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Therapy Session', {
- setup: function(frm) {
- frm.get_field('exercises').grid.editable_fields = [
- {fieldname: 'exercise_type', columns: 7},
- {fieldname: 'counts_target', columns: 1},
- {fieldname: 'counts_completed', columns: 1},
- {fieldname: 'assistance_level', columns: 1}
- ];
-
- frm.set_query('service_unit', function() {
- return {
- filters: {
- 'is_group': false,
- 'allow_appointments': true,
- 'company': frm.doc.company
- }
- };
- });
-
- frm.set_query('appointment', function() {
-
- return {
- filters: {
- 'status': ['in', ['Open', 'Scheduled']]
- }
- };
- });
- },
-
- refresh: function(frm) {
- if (frm.doc.therapy_plan) {
- frm.trigger('filter_therapy_types');
- }
-
- if (!frm.doc.__islocal) {
- frm.dashboard.add_indicator(__('Counts Targeted: {0}', [frm.doc.total_counts_targeted]), 'blue');
- frm.dashboard.add_indicator(__('Counts Completed: {0}', [frm.doc.total_counts_completed]),
- (frm.doc.total_counts_completed < frm.doc.total_counts_targeted) ? 'orange' : 'green');
- }
-
- if (frm.doc.docstatus === 1) {
- frm.add_custom_button(__('Patient Assessment'), function() {
- frappe.model.open_mapped_doc({
- method: 'erpnext.healthcare.doctype.patient_assessment.patient_assessment.create_patient_assessment',
- frm: frm,
- })
- }, 'Create');
-
- frappe.db.get_value('Therapy Plan', {'name': frm.doc.therapy_plan}, 'therapy_plan_template', (r) => {
- if (r && !r.therapy_plan_template) {
- frm.add_custom_button(__('Sales Invoice'), function() {
- frappe.model.open_mapped_doc({
- method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.invoice_therapy_session',
- frm: frm,
- });
- }, 'Create');
- }
- });
- }
- },
-
- therapy_plan: function(frm) {
- if (frm.doc.therapy_plan) {
- frm.trigger('filter_therapy_types');
- }
- },
-
- filter_therapy_types: function(frm) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Therapy Plan',
- name: frm.doc.therapy_plan
- },
- callback: function(data) {
- let therapy_types = (data.message.therapy_plan_details || []).map(function(d){ return d.therapy_type; });
- frm.set_query('therapy_type', function() {
- return {
- filters: { 'therapy_type': ['in', therapy_types]}
- };
- });
- }
- });
- },
-
- patient: function(frm) {
- if (frm.doc.patient) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
- args: {
- patient: frm.doc.patient
- },
- callback: function (data) {
- let age = '';
- if (data.message.dob) {
- age = calculate_age(data.message.dob);
- } else if (data.message.age) {
- age = data.message.age;
- if (data.message.age_as_on) {
- age = __('{0} as on {1}', [age, data.message.age_as_on]);
- }
- }
- frm.set_value('patient_age', age);
- frm.set_value('gender', data.message.sex);
- frm.set_value('patient_name', data.message.patient_name);
- }
- });
- } else {
- frm.set_value('patient_age', '');
- frm.set_value('gender', '');
- frm.set_value('patient_name', '');
- }
- },
-
- appointment: function(frm) {
- if (frm.doc.appointment) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Patient Appointment',
- name: frm.doc.appointment
- },
- callback: function(data) {
- let values = {
- 'patient':data.message.patient,
- 'therapy_type': data.message.therapy_type,
- 'therapy_plan': data.message.therapy_plan,
- 'practitioner': data.message.practitioner,
- 'department': data.message.department,
- 'start_date': data.message.appointment_date,
- 'start_time': data.message.appointment_time,
- 'service_unit': data.message.service_unit,
- 'company': data.message.company,
- 'duration': data.message.duration
- };
- frm.set_value(values);
- }
- });
- }
- },
-
- therapy_type: function(frm) {
- if (frm.doc.therapy_type) {
- frappe.call({
- 'method': 'frappe.client.get',
- args: {
- doctype: 'Therapy Type',
- name: frm.doc.therapy_type
- },
- callback: function(data) {
- frm.set_value('duration', data.message.default_duration);
- frm.set_value('rate', data.message.rate);
- frm.set_value('service_unit', data.message.healthcare_service_unit);
- frm.set_value('department', data.message.medical_department);
- frm.doc.exercises = [];
- $.each(data.message.exercises, function(_i, e) {
- let exercise = frm.add_child('exercises');
- exercise.exercise_type = e.exercise_type;
- exercise.difficulty_level = e.difficulty_level;
- exercise.counts_target = e.counts_target;
- exercise.assistance_level = e.assistance_level;
- });
- refresh_field('exercises');
- }
- });
- }
- }
-});
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.json b/erpnext/healthcare/doctype/therapy_session/therapy_session.json
deleted file mode 100644
index 0bb2b0e..0000000
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.json
+++ /dev/null
@@ -1,264 +0,0 @@
-{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2020-03-11 08:57:40.669857",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "appointment",
- "patient",
- "patient_name",
- "patient_age",
- "gender",
- "column_break_5",
- "company",
- "therapy_plan",
- "therapy_type",
- "practitioner",
- "department",
- "details_section",
- "medical_code",
- "duration",
- "rate",
- "location",
- "column_break_12",
- "service_unit",
- "start_date",
- "start_time",
- "invoiced",
- "exercises_section",
- "exercises",
- "section_break_23",
- "total_counts_targeted",
- "column_break_25",
- "total_counts_completed",
- "amended_from"
- ],
- "fields": [
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-THP-.YYYY.-"
- },
- {
- "fieldname": "appointment",
- "fieldtype": "Link",
- "label": "Appointment",
- "options": "Patient Appointment",
- "set_only_once": 1
- },
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fetch_from": "patient.sex",
- "fieldname": "gender",
- "fieldtype": "Link",
- "label": "Gender",
- "options": "Gender",
- "read_only": 1
- },
- {
- "fieldname": "column_break_5",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner"
- },
- {
- "fieldname": "department",
- "fieldtype": "Link",
- "label": "Medical Department",
- "options": "Medical Department"
- },
- {
- "fieldname": "details_section",
- "fieldtype": "Section Break",
- "label": "Details"
- },
- {
- "fetch_from": "therapy_template.default_duration",
- "fieldname": "duration",
- "fieldtype": "Int",
- "label": "Duration",
- "reqd": 1
- },
- {
- "fieldname": "location",
- "fieldtype": "Select",
- "label": "Location",
- "options": "\nCenter\nHome\nTele"
- },
- {
- "fieldname": "column_break_12",
- "fieldtype": "Column Break"
- },
- {
- "fetch_from": "therapy_template.rate",
- "fieldname": "rate",
- "fieldtype": "Currency",
- "label": "Rate"
- },
- {
- "fieldname": "exercises_section",
- "fieldtype": "Section Break",
- "label": "Exercises"
- },
- {
- "fieldname": "exercises",
- "fieldtype": "Table",
- "label": "Exercises",
- "options": "Exercise"
- },
- {
- "depends_on": "eval: doc.therapy_plan",
- "fieldname": "therapy_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Therapy Type",
- "options": "Therapy Type",
- "reqd": 1
- },
- {
- "fieldname": "therapy_plan",
- "fieldtype": "Link",
- "label": "Therapy Plan",
- "options": "Therapy Plan",
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Therapy Session",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "service_unit",
- "fieldtype": "Link",
- "label": "Healthcare Service Unit",
- "options": "Healthcare Service Unit"
- },
- {
- "fieldname": "start_date",
- "fieldtype": "Date",
- "label": "Start Date",
- "reqd": 1
- },
- {
- "fieldname": "start_time",
- "fieldtype": "Time",
- "label": "Start Time"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "invoiced",
- "fieldtype": "Check",
- "label": "Invoiced",
- "read_only": 1
- },
- {
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "label": "Patient Age",
- "read_only": 1
- },
- {
- "fieldname": "total_counts_targeted",
- "fieldtype": "Int",
- "label": "Total Counts Targeted",
- "read_only": 1
- },
- {
- "fieldname": "total_counts_completed",
- "fieldtype": "Int",
- "label": "Total Counts Completed",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "section_break_23",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "column_break_25",
- "fieldtype": "Column Break"
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fetch_from": "therapy_type.medical_code",
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "label": "Medical Code",
- "options": "Medical Code",
- "read_only": 1
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-11-04 18:14:25.999939",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Therapy Session",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "search_fields": "patient,appointment,therapy_plan,therapy_type",
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.py b/erpnext/healthcare/doctype/therapy_session/therapy_session.py
deleted file mode 100644
index 51f267f..0000000
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-import datetime
-from frappe.model.document import Document
-from frappe.utils import get_time, flt
-from frappe.model.mapper import get_mapped_doc
-from frappe import _
-from frappe.utils import cstr, getdate, get_link_to_form
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
-
-class TherapySession(Document):
- def validate(self):
- self.validate_duplicate()
- self.set_total_counts()
-
- def validate_duplicate(self):
- end_time = datetime.datetime.combine(getdate(self.start_date), get_time(self.start_time)) \
- + datetime.timedelta(minutes=flt(self.duration))
-
- overlaps = frappe.db.sql("""
- select
- name
- from
- `tabTherapy Session`
- where
- start_date=%s and name!=%s and docstatus!=2
- and (practitioner=%s or patient=%s) and
- ((start_time<%s and start_time + INTERVAL duration MINUTE>%s) or
- (start_time>%s and start_time<%s) or
- (start_time=%s))
- """, (self.start_date, self.name, self.practitioner, self.patient,
- self.start_time, end_time.time(), self.start_time, end_time.time(), self.start_time))
-
- if overlaps:
- overlapping_details = _('Therapy Session overlaps with {0}').format(get_link_to_form('Therapy Session', overlaps[0][0]))
- frappe.throw(overlapping_details, title=_('Therapy Sessions Overlapping'))
-
- def on_submit(self):
- self.update_sessions_count_in_therapy_plan()
-
- def on_update(self):
- if self.appointment:
- frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
-
- def on_cancel(self):
- if self.appointment:
- frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
-
- self.update_sessions_count_in_therapy_plan(on_cancel=True)
-
- def update_sessions_count_in_therapy_plan(self, on_cancel=False):
- therapy_plan = frappe.get_doc('Therapy Plan', self.therapy_plan)
- for entry in therapy_plan.therapy_plan_details:
- if entry.therapy_type == self.therapy_type:
- if on_cancel:
- entry.sessions_completed -= 1
- else:
- entry.sessions_completed += 1
- therapy_plan.save()
-
- def set_total_counts(self):
- target_total = 0
- counts_completed = 0
- for entry in self.exercises:
- if entry.counts_target:
- target_total += entry.counts_target
- if entry.counts_completed:
- counts_completed += entry.counts_completed
-
- self.db_set('total_counts_targeted', target_total)
- self.db_set('total_counts_completed', counts_completed)
-
-
-@frappe.whitelist()
-def create_therapy_session(source_name, target_doc=None):
- def set_missing_values(source, target):
- therapy_type = frappe.get_doc('Therapy Type', source.therapy_type)
- target.exercises = therapy_type.exercises
-
- doc = get_mapped_doc('Patient Appointment', source_name, {
- 'Patient Appointment': {
- 'doctype': 'Therapy Session',
- 'field_map': [
- ['appointment', 'name'],
- ['patient', 'patient'],
- ['patient_age', 'patient_age'],
- ['gender', 'patient_sex'],
- ['therapy_type', 'therapy_type'],
- ['therapy_plan', 'therapy_plan'],
- ['practitioner', 'practitioner'],
- ['department', 'department'],
- ['start_date', 'appointment_date'],
- ['start_time', 'appointment_time'],
- ['service_unit', 'service_unit'],
- ['company', 'company'],
- ['invoiced', 'invoiced']
- ]
- }
- }, target_doc, set_missing_values)
-
- return doc
-
-
-@frappe.whitelist()
-def invoice_therapy_session(source_name, target_doc=None):
- def set_missing_values(source, target):
- target.customer = frappe.db.get_value('Patient', source.patient, 'customer')
- target.due_date = getdate()
- target.debit_to = get_receivable_account(source.company)
- item = target.append('items', {})
- item = get_therapy_item(source, item)
- target.set_missing_values(for_validate=True)
-
- doc = get_mapped_doc('Therapy Session', source_name, {
- 'Therapy Session': {
- 'doctype': 'Sales Invoice',
- 'field_map': [
- ['patient', 'patient'],
- ['referring_practitioner', 'practitioner'],
- ['company', 'company'],
- ['due_date', 'start_date']
- ]
- }
- }, target_doc, set_missing_values)
-
- return doc
-
-
-def get_therapy_item(therapy, item):
- item.item_code = frappe.db.get_value('Therapy Type', therapy.therapy_type, 'item')
- item.description = _('Therapy Session Charges: {0}').format(therapy.practitioner)
- item.income_account = get_income_account(therapy.practitioner, therapy.company)
- item.cost_center = frappe.get_cached_value('Company', therapy.company, 'cost_center')
- item.rate = therapy.rate
- item.amount = therapy.rate
- item.qty = 1
- item.reference_dt = 'Therapy Session'
- item.reference_dn = therapy.name
- return item
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session_dashboard.py b/erpnext/healthcare/doctype/therapy_session/therapy_session_dashboard.py
deleted file mode 100644
index 9de7e29..0000000
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session_dashboard.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'therapy_session',
- 'transactions': [
- {
- 'label': _('Assessments'),
- 'items': ['Patient Assessment']
- }
- ]
- }
diff --git a/erpnext/healthcare/doctype/therapy_type/__init__.py b/erpnext/healthcare/doctype/therapy_type/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/therapy_type/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py
deleted file mode 100644
index 21f6369..0000000
--- a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestTherapyType(unittest.TestCase):
- def test_therapy_type_item(self):
- therapy_type = create_therapy_type()
- self.assertTrue(frappe.db.exists('Item', therapy_type.item))
-
- therapy_type.disabled = 1
- therapy_type.save()
- self.assertEqual(frappe.db.get_value('Item', therapy_type.item, 'disabled'), 1)
-
-def create_therapy_type():
- exercise = create_exercise_type()
- therapy_type = frappe.db.exists('Therapy Type', 'Basic Rehab')
- if not therapy_type:
- therapy_type = frappe.new_doc('Therapy Type')
- therapy_type.therapy_type = 'Basic Rehab'
- therapy_type.default_duration = 30
- therapy_type.is_billable = 1
- therapy_type.rate = 5000
- therapy_type.item_code = 'Basic Rehab'
- therapy_type.item_name = 'Basic Rehab'
- therapy_type.item_group = 'Services'
- therapy_type.append('exercises', {
- 'exercise_type': exercise.name,
- 'counts_target': 10,
- 'assistance_level': 'Passive'
- })
- therapy_type.save()
- else:
- therapy_type = frappe.get_doc('Therapy Type', 'Basic Rehab')
- return therapy_type
-
-def create_exercise_type():
- exercise_type = frappe.db.exists('Exercise Type', 'Sit to Stand')
- if not exercise_type:
- exercise_type = frappe.new_doc('Exercise Type')
- exercise_type.exercise_name = 'Sit to Stand'
- exercise_type.append('steps_table', {
- 'title': 'Step 1',
- 'description': 'Squat and Rise'
- })
- exercise_type.save()
- return exercise_type
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/therapy_type/therapy_type.js b/erpnext/healthcare/doctype/therapy_type/therapy_type.js
deleted file mode 100644
index 6e155dc..0000000
--- a/erpnext/healthcare/doctype/therapy_type/therapy_type.js
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Therapy Type', {
- setup: function(frm) {
- frm.get_field('exercises').grid.editable_fields = [
- {fieldname: 'exercise_type', columns: 7},
- {fieldname: 'difficulty_level', columns: 1},
- {fieldname: 'counts_target', columns: 1},
- {fieldname: 'assistance_level', columns: 1}
- ];
- },
-
- refresh: function(frm) {
- if (!frm.doc.__islocal) {
- cur_frm.add_custom_button(__('Change Item Code'), function() {
- change_template_code(frm.doc);
- });
- }
- },
-
- therapy_type: function(frm) {
- if (!frm.doc.item_code)
- frm.set_value('item_code', frm.doc.therapy_type);
- if (!frm.doc.description)
- frm.set_value('description', frm.doc.therapy_type);
- mark_change_in_item(frm);
- },
-
- rate: function(frm) {
- mark_change_in_item(frm);
- },
-
- is_billable: function (frm) {
- mark_change_in_item(frm);
- },
-
- item_group: function(frm) {
- mark_change_in_item(frm);
- },
-
- description: function(frm) {
- mark_change_in_item(frm);
- },
-
- medical_department: function(frm) {
- mark_change_in_item(frm);
- },
-
- medical_code: function(frm) {
- frm.set_query("medical_code", function() {
- return {
- filters: {
- medical_code_standard: frm.doc.medical_code_standard
- }
- };
- });
- }
-});
-
-let mark_change_in_item = function(frm) {
- if (!frm.doc.__islocal) {
- frm.doc.change_in_item = 1;
- }
-};
-
-let change_template_code = function(doc) {
- let d = new frappe.ui.Dialog({
- title:__('Change Item Code'),
- fields:[
- {
- 'fieldtype': 'Data',
- 'label': 'Item Code',
- 'fieldname': 'item_code',
- reqd: 1
- }
- ],
- primary_action: function() {
- let values = d.get_values();
-
- if (values) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.therapy_type.therapy_type.change_item_code_from_therapy',
- 'args': {item_code: values.item_code, doc: doc},
- callback: function () {
- cur_frm.reload_doc();
- frappe.show_alert({
- message: 'Item Code renamed successfully',
- indicator: 'green'
- });
- }
- });
- }
- d.hide();
- },
- primary_action_label: __('Change Item Code')
- });
- d.show();
-
- d.set_values({
- 'item_code': doc.item_code
- });
-};
diff --git a/erpnext/healthcare/doctype/therapy_type/therapy_type.json b/erpnext/healthcare/doctype/therapy_type/therapy_type.json
deleted file mode 100644
index f365b1d..0000000
--- a/erpnext/healthcare/doctype/therapy_type/therapy_type.json
+++ /dev/null
@@ -1,234 +0,0 @@
-{
- "actions": [],
- "autoname": "field:therapy_type",
- "creation": "2020-03-29 20:48:31.715063",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "disabled",
- "section_break_2",
- "therapy_type",
- "default_duration",
- "medical_department",
- "column_break_3",
- "is_billable",
- "rate",
- "healthcare_service_unit",
- "item_details_section",
- "item",
- "item_code",
- "item_name",
- "item_group",
- "column_break_12",
- "description",
- "medical_coding_section",
- "medical_code_standard",
- "medical_code",
- "section_break_18",
- "therapy_for",
- "add_exercises",
- "section_break_6",
- "exercises",
- "change_in_item"
- ],
- "fields": [
- {
- "fieldname": "therapy_type",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Therapy Type",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "is_billable",
- "fieldtype": "Check",
- "label": "Is Billable"
- },
- {
- "depends_on": "eval:doc.is_billable;",
- "fieldname": "rate",
- "fieldtype": "Currency",
- "label": "Rate",
- "mandatory_depends_on": "eval:doc.is_billable;"
- },
- {
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "label": "Exercises"
- },
- {
- "fieldname": "exercises",
- "fieldtype": "Table",
- "label": "Exercises",
- "options": "Exercise"
- },
- {
- "fieldname": "default_duration",
- "fieldtype": "Int",
- "label": "Default Duration (In Minutes)"
- },
- {
- "default": "0",
- "fieldname": "disabled",
- "fieldtype": "Check",
- "label": "Disabled"
- },
- {
- "fieldname": "item_details_section",
- "fieldtype": "Section Break",
- "label": "Item Details"
- },
- {
- "fieldname": "item",
- "fieldtype": "Link",
- "label": "Item",
- "options": "Item",
- "read_only": 1
- },
- {
- "fieldname": "item_code",
- "fieldtype": "Data",
- "label": "Item Code",
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "item_group",
- "fieldtype": "Link",
- "label": "Item Group",
- "options": "Item Group",
- "reqd": 1
- },
- {
- "fieldname": "item_name",
- "fieldtype": "Data",
- "label": "Item Name",
- "reqd": 1
- },
- {
- "fieldname": "column_break_12",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "description",
- "fieldtype": "Small Text",
- "label": "Description"
- },
- {
- "fieldname": "section_break_2",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "medical_department",
- "fieldtype": "Link",
- "label": "Medical Department",
- "options": "Medical Department"
- },
- {
- "default": "0",
- "fieldname": "change_in_item",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Change In Item",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "therapy_for",
- "fieldtype": "Table MultiSelect",
- "label": "Therapy For",
- "options": "Body Part Link"
- },
- {
- "fieldname": "healthcare_service_unit",
- "fieldtype": "Link",
- "label": "Healthcare Service Unit",
- "options": "Healthcare Service Unit"
- },
- {
- "depends_on": "eval: doc.therapy_for",
- "fieldname": "add_exercises",
- "fieldtype": "Button",
- "label": "Add Exercises",
- "options": "add_exercises"
- },
- {
- "fieldname": "section_break_18",
- "fieldtype": "Section Break"
- },
- {
- "collapsible": 1,
- "fieldname": "medical_coding_section",
- "fieldtype": "Section Break",
- "label": "Medical Coding",
- "options": "Medical Coding"
- },
- {
- "fieldname": "medical_code_standard",
- "fieldtype": "Link",
- "label": "Medical Code Standard",
- "options": "Medical Code Standard"
- },
- {
- "depends_on": "medical_code_standard",
- "fieldname": "medical_code",
- "fieldtype": "Link",
- "label": "Medical Code",
- "options": "Medical Code"
- }
- ],
- "links": [],
- "modified": "2020-06-29 14:18:50.669951",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Therapy Type",
- "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": "Healthcare Administrator",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "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/healthcare/doctype/therapy_type/therapy_type.py b/erpnext/healthcare/doctype/therapy_type/therapy_type.py
deleted file mode 100644
index 3f6a36a..0000000
--- a/erpnext/healthcare/doctype/therapy_type/therapy_type.py
+++ /dev/null
@@ -1,123 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe import _
-from frappe.utils import cint
-from frappe.model.document import Document
-from frappe.model.rename_doc import rename_doc
-
-class TherapyType(Document):
- def validate(self):
- self.enable_disable_item()
-
- def after_insert(self):
- create_item_from_therapy(self)
-
- def on_update(self):
- if self.change_in_item:
- self.update_item_and_item_price()
-
- def enable_disable_item(self):
- if self.is_billable:
- if self.disabled:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
- else:
- frappe.db.set_value('Item', self.item, 'disabled', 0)
-
- def update_item_and_item_price(self):
- if self.is_billable and self.item:
- item_doc = frappe.get_doc('Item', {'item_code': self.item})
- item_doc.item_name = self.item_name
- item_doc.item_group = self.item_group
- item_doc.description = self.description
- item_doc.disabled = 0
- item_doc.ignore_mandatory = True
- item_doc.save(ignore_permissions=True)
-
- if self.rate:
- item_price = frappe.get_doc('Item Price', {'item_code': self.item})
- item_price.item_name = self.item_name
- item_price.price_list_rate = self.rate
- item_price.ignore_mandatory = True
- item_price.save()
-
- elif not self.is_billable and self.item:
- frappe.db.set_value('Item', self.item, 'disabled', 1)
-
- self.db_set('change_in_item', 0)
-
- @frappe.whitelist()
- def add_exercises(self):
- exercises = self.get_exercises_for_body_parts()
- last_idx = max([cint(d.idx) for d in self.get('exercises')] or [0,])
- for i, d in enumerate(exercises):
- ch = self.append('exercises', {})
- ch.exercise_type = d.parent
- ch.idx = last_idx + i + 1
-
- def get_exercises_for_body_parts(self):
- body_parts = [entry.body_part for entry in self.therapy_for]
-
- exercises = frappe.db.sql(
- """
- SELECT DISTINCT
- b.parent, e.name, e.difficulty_level
- FROM
- `tabExercise Type` e, `tabBody Part Link` b
- WHERE
- b.body_part IN %(body_parts)s AND b.parent=e.name
- """, {'body_parts': body_parts}, as_dict=1)
-
- return exercises
-
-
-def create_item_from_therapy(doc):
- disabled = doc.disabled
- if doc.is_billable and not doc.disabled:
- disabled = 0
-
- uom = frappe.db.exists('UOM', 'Unit') or frappe.db.get_single_value('Stock Settings', 'stock_uom')
-
- item = frappe.get_doc({
- 'doctype': 'Item',
- 'item_code': doc.item_code,
- 'item_name': doc.item_name,
- 'item_group': doc.item_group,
- 'description': doc.description,
- 'is_sales_item': 1,
- 'is_service_item': 1,
- 'is_purchase_item': 0,
- 'is_stock_item': 0,
- 'show_in_website': 0,
- 'is_pro_applicable': 0,
- 'disabled': disabled,
- 'stock_uom': uom
- }).insert(ignore_permissions=True, ignore_mandatory=True)
-
- make_item_price(item.name, doc.rate)
- doc.db_set('item', item.name)
-
-
-def make_item_price(item, item_price):
- price_list_name = frappe.db.get_value('Price List', {'selling': 1})
- 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.whitelist()
-def change_item_code_from_therapy(item_code, doc):
- doc = frappe._dict(json.loads(doc))
-
- if frappe.db.exists('Item', {'item_code': item_code}):
- frappe.throw(_('Item with Item Code {0} already exists').format(item_code))
- else:
- rename_doc('Item', doc.item, item_code, ignore_permissions=True)
- frappe.db.set_value('Therapy Type', doc.name, 'item_code', item_code)
- return
diff --git a/erpnext/healthcare/doctype/vital_signs/__init__.py b/erpnext/healthcare/doctype/vital_signs/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/doctype/vital_signs/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js
deleted file mode 100644
index f4ab446..0000000
--- a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Vital Signs", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Vital Signs
- () => frappe.tests.make('Vital Signs', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.py b/erpnext/healthcare/doctype/vital_signs/test_vital_signs.py
deleted file mode 100644
index 5d3e007..0000000
--- a/erpnext/healthcare/doctype/vital_signs/test_vital_signs.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-# test_records = frappe.get_test_records('Vital Signs')
-
-class TestVitalSigns(unittest.TestCase):
- pass
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.js b/erpnext/healthcare/doctype/vital_signs/vital_signs.js
deleted file mode 100644
index 78509e0..0000000
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.js
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2016, ESS LLP and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Vital Signs', {
- height: function(frm) {
- if (frm.doc.height && frm.doc.weight) {
- calculate_bmi(frm);
- }
- },
-
- weight: function(frm) {
- if (frm.doc.height && frm.doc.weight) {
- calculate_bmi(frm);
- }
- },
-
- bp_systolic: function(frm) {
- if (frm.doc.bp_systolic && frm.doc.bp_diastolic) {
- set_bp(frm);
- }
- },
-
- bp_diastolic: function(frm) {
- if (frm.doc.bp_systolic && frm.doc.bp_diastolic) {
- set_bp(frm);
- }
- }
-});
-
-let calculate_bmi = function(frm){
- // Reference https://en.wikipedia.org/wiki/Body_mass_index
- // bmi = weight (in Kg) / height * height (in Meter)
- let bmi = (frm.doc.weight / (frm.doc.height * frm.doc.height)).toFixed(2);
- let bmi_note = null;
-
- if (bmi<18.5) {
- bmi_note = 'Underweight';
- } else if (bmi>=18.5 && bmi<25) {
- bmi_note = 'Normal';
- } else if (bmi>=25 && bmi<30) {
- bmi_note = 'Overweight';
- } else if (bmi>=30) {
- bmi_note = 'Obese';
- }
- frappe.model.set_value(frm.doctype,frm.docname, 'bmi', bmi);
- frappe.model.set_value(frm.doctype,frm.docname, 'nutrition_note', bmi_note);
-};
-
-let set_bp = function(frm){
- let bp = frm.doc.bp_systolic+ '/' + frm.doc.bp_diastolic + ' mmHg';
- frappe.model.set_value(frm.doctype,frm.docname, 'bp', bp);
-};
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json
deleted file mode 100644
index 15ab504..0000000
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json
+++ /dev/null
@@ -1,305 +0,0 @@
-{
- "actions": [],
- "allow_copy": 1,
- "allow_import": 1,
- "autoname": "naming_series:",
- "beta": 1,
- "creation": "2017-02-02 11:00:24.853005",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "title",
- "patient",
- "patient_name",
- "inpatient_record",
- "appointment",
- "encounter",
- "column_break_2",
- "company",
- "signs_date",
- "signs_time",
- "sb_vs",
- "temperature",
- "pulse",
- "respiratory_rate",
- "tongue",
- "abdomen",
- "column_break_8",
- "reflexes",
- "bp_systolic",
- "bp_diastolic",
- "bp",
- "vital_signs_note",
- "sb_nutrition_values",
- "height",
- "weight",
- "bmi",
- "column_break_14",
- "nutrition_note",
- "sb_references",
- "amended_from"
- ],
- "fields": [
- {
- "fetch_from": "patient.inpatient_record",
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "read_only": 1
- },
- {
- "fetch_from": "inpatient_record.patient",
- "fieldname": "patient",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Patient",
- "options": "Patient",
- "reqd": 1
- },
- {
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "label": "Patient Name",
- "read_only": 1
- },
- {
- "fieldname": "appointment",
- "fieldtype": "Link",
- "in_filter": 1,
- "label": "Patient Appointment",
- "no_copy": 1,
- "options": "Patient Appointment",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "encounter",
- "fieldtype": "Link",
- "in_filter": 1,
- "label": "Patient Encounter",
- "no_copy": 1,
- "options": "Patient Encounter",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "column_break_2",
- "fieldtype": "Column Break"
- },
- {
- "default": "Today",
- "fieldname": "signs_date",
- "fieldtype": "Date",
- "label": "Date",
- "reqd": 1
- },
- {
- "fieldname": "signs_time",
- "fieldtype": "Time",
- "label": "Time",
- "reqd": 1
- },
- {
- "fieldname": "sb_vs",
- "fieldtype": "Section Break",
- "label": "Vital Signs"
- },
- {
- "description": "Presence of a fever (temp > 38.5 \u00b0C/101.3 \u00b0F or sustained temp > 38 \u00b0C/100.4 \u00b0F)",
- "fieldname": "temperature",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Body Temperature"
- },
- {
- "description": "Adults' pulse rate is anywhere between 50 and 80 beats per minute.",
- "fieldname": "pulse",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Heart Rate / Pulse"
- },
- {
- "description": "Normal reference range for an adult is 16\u201320 breaths/minute (RCP 2012)",
- "fieldname": "respiratory_rate",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Respiratory rate"
- },
- {
- "fieldname": "tongue",
- "fieldtype": "Select",
- "label": "Tongue",
- "options": "\nCoated\nVery Coated\nNormal\nFurry\nCuts"
- },
- {
- "fieldname": "abdomen",
- "fieldtype": "Select",
- "label": "Abdomen",
- "options": "\nNormal\nBloated\nFull\nFluid\nConstipated"
- },
- {
- "fieldname": "column_break_8",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "reflexes",
- "fieldtype": "Select",
- "label": "Reflexes",
- "options": "\nNormal\nHyper\nVery Hyper\nOne Sided"
- },
- {
- "fieldname": "bp_systolic",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Blood Pressure (systolic)"
- },
- {
- "fieldname": "bp_diastolic",
- "fieldtype": "Data",
- "ignore_xss_filter": 1,
- "in_list_view": 1,
- "label": "Blood Pressure (diastolic)"
- },
- {
- "description": "Normal resting blood pressure in an adult is approximately 120 mmHg systolic, and 80 mmHg diastolic, abbreviated \"120/80 mmHg\"",
- "fieldname": "bp",
- "fieldtype": "Data",
- "label": "Blood Pressure",
- "read_only": 1
- },
- {
- "fieldname": "vital_signs_note",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Notes"
- },
- {
- "fieldname": "sb_nutrition_values",
- "fieldtype": "Section Break",
- "label": "Nutrition Values"
- },
- {
- "fieldname": "height",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Height (In Meter)"
- },
- {
- "fieldname": "weight",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Weight (In Kilogram)"
- },
- {
- "default": "0.00",
- "fieldname": "bmi",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "BMI",
- "read_only": 1
- },
- {
- "fieldname": "column_break_14",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "nutrition_note",
- "fieldtype": "Small Text",
- "ignore_xss_filter": 1,
- "label": "Notes"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company"
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Vital Signs",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "sb_references",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "HLC-VTS-.YYYY.-",
- "reqd": 1
- },
- {
- "allow_on_submit": 1,
- "columns": 5,
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- }
- ],
- "is_submittable": 1,
- "links": [],
- "modified": "2020-05-17 22:23:24.632286",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Vital Signs",
- "owner": "Administrator",
- "permissions": [
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Physician",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Nursing User",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient, signs_date",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "title",
- "track_changes": 1,
- "track_seen": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
deleted file mode 100644
index 35c823d..0000000
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe.utils import cstr
-from frappe import _
-
-class VitalSigns(Document):
- def validate(self):
- self.set_title()
-
- def set_title(self):
- self.title = _('{0} on {1}').format(self.patient_name or self.patient,
- frappe.utils.format_date(self.signs_date))[:100]
-
diff --git a/erpnext/healthcare/healthcare_dashboard/healthcare/healthcare.json b/erpnext/healthcare/healthcare_dashboard/healthcare/healthcare.json
deleted file mode 100644
index 2fea668..0000000
--- a/erpnext/healthcare/healthcare_dashboard/healthcare/healthcare.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- "cards": [
- {
- "card": "Total Patients"
- },
- {
- "card": "Total Patients Admitted"
- },
- {
- "card": "Open Appointments"
- },
- {
- "card": "Appointments to Bill"
- }
- ],
- "charts": [
- {
- "chart": "Patient Appointments",
- "width": "Full"
- },
- {
- "chart": "In-Patient Status",
- "width": "Half"
- },
- {
- "chart": "Clinical Procedures Status",
- "width": "Half"
- },
- {
- "chart": "Lab Tests",
- "width": "Half"
- },
- {
- "chart": "Clinical Procedures",
- "width": "Half"
- },
- {
- "chart": "Symptoms",
- "width": "Half"
- },
- {
- "chart": "Diagnoses",
- "width": "Half"
- },
- {
- "chart": "Department wise Patient Appointments",
- "width": "Full"
- }
- ],
- "creation": "2020-07-14 18:17:54.823311",
- "dashboard_name": "Healthcare",
- "docstatus": 0,
- "doctype": "Dashboard",
- "idx": 0,
- "is_default": 0,
- "is_standard": 1,
- "modified": "2020-07-22 15:36:34.220387",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare",
- "owner": "Administrator"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json
deleted file mode 100644
index 56c3c13..0000000
--- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "allow_roles": [
- {
- "role": "Healthcare Administrator"
- }
- ],
- "creation": "2020-05-19 10:32:43.025852",
- "docstatus": 0,
- "doctype": "Module Onboarding",
- "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare",
- "idx": 0,
- "is_complete": 0,
- "modified": "2020-07-08 14:06:19.512946",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare",
- "owner": "Administrator",
- "steps": [
- {
- "step": "Create Patient"
- },
- {
- "step": "Create Practitioner Schedule"
- },
- {
- "step": "Introduction to Healthcare Practitioner"
- },
- {
- "step": "Create Healthcare Practitioner"
- },
- {
- "step": "Explore Healthcare Settings"
- },
- {
- "step": "Explore Clinical Procedure Templates"
- }
- ],
- "subtitle": "Patients, Practitioner Schedules, Settings, and more.",
- "success_message": "The Healthcare Module is all set up!",
- "title": "Let's Set Up the Healthcare Module."
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/number_card/appointments_to_bill/appointments_to_bill.json b/erpnext/healthcare/number_card/appointments_to_bill/appointments_to_bill.json
deleted file mode 100644
index 3e4d4e2..0000000
--- a/erpnext/healthcare/number_card/appointments_to_bill/appointments_to_bill.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "creation": "2020-07-14 18:17:54.792773",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Patient Appointment",
- "dynamic_filters_json": "[[\"Patient Appointment\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[[\"Patient Appointment\",\"invoiced\",\"=\",0,false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Appointments To Bill",
- "modified": "2020-07-22 13:27:58.038577",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Appointments to Bill",
- "owner": "Administrator",
- "show_percentage_stats": 1,
- "stats_time_interval": "Daily",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/number_card/open_appointments/open_appointments.json b/erpnext/healthcare/number_card/open_appointments/open_appointments.json
deleted file mode 100644
index 8d121cc..0000000
--- a/erpnext/healthcare/number_card/open_appointments/open_appointments.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "creation": "2020-07-14 18:17:54.771092",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Patient Appointment",
- "dynamic_filters_json": "[[\"Patient Appointment\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
- "filters_json": "[[\"Patient Appointment\",\"status\",\"=\",\"Open\",false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Open Appointments",
- "modified": "2020-07-22 13:27:09.542122",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Open Appointments",
- "owner": "Administrator",
- "show_percentage_stats": 1,
- "stats_time_interval": "Daily",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/number_card/total_patients/total_patients.json b/erpnext/healthcare/number_card/total_patients/total_patients.json
deleted file mode 100644
index 75441a6..0000000
--- a/erpnext/healthcare/number_card/total_patients/total_patients.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "creation": "2020-07-14 18:17:54.727946",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Patient",
- "filters_json": "[[\"Patient\",\"status\",\"=\",\"Active\",false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Total Patients",
- "modified": "2020-07-22 13:26:02.643534",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Total Patients",
- "owner": "Administrator",
- "show_percentage_stats": 1,
- "stats_time_interval": "Daily",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/number_card/total_patients_admitted/total_patients_admitted.json b/erpnext/healthcare/number_card/total_patients_admitted/total_patients_admitted.json
deleted file mode 100644
index 69a967d..0000000
--- a/erpnext/healthcare/number_card/total_patients_admitted/total_patients_admitted.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "creation": "2020-07-14 18:17:54.749754",
- "docstatus": 0,
- "doctype": "Number Card",
- "document_type": "Patient",
- "filters_json": "[[\"Patient\",\"inpatient_status\",\"=\",\"Admitted\",false]]",
- "function": "Count",
- "idx": 0,
- "is_public": 1,
- "is_standard": 1,
- "label": "Total Patients Admitted",
- "modified": "2020-07-22 13:26:20.027788",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Total Patients Admitted",
- "owner": "Administrator",
- "show_percentage_stats": 1,
- "stats_time_interval": "Daily",
- "type": "Document Type"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json
deleted file mode 100644
index c45a347..0000000
--- a/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-05-19 10:39:55.728058",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-05-26 23:16:31.965521",
- "modified_by": "Administrator",
- "name": "Create Healthcare Practitioner",
- "owner": "Administrator",
- "reference_document": "Healthcare Practitioner",
- "show_full_form": 1,
- "title": "Create Healthcare Practitioner",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json
deleted file mode 100644
index 77bc5bd..0000000
--- a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-05-19 10:32:27.648902",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-05-19 12:26:24.023418",
- "modified_by": "Administrator",
- "name": "Create Patient",
- "owner": "Administrator",
- "reference_document": "Patient",
- "show_full_form": 1,
- "title": "Create Patient",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json
deleted file mode 100644
index 65980ef..0000000
--- a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-05-19 10:41:19.065753",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-05-19 12:27:09.437825",
- "modified_by": "Administrator",
- "name": "Create Practitioner Schedule",
- "owner": "Administrator",
- "reference_document": "Practitioner Schedule",
- "show_full_form": 1,
- "title": "Create Practitioner Schedule",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json
deleted file mode 100644
index 697b761..0000000
--- a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Show Form Tour",
- "creation": "2020-05-19 11:40:51.963741",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-05-26 23:10:24.504030",
- "modified_by": "Administrator",
- "name": "Explore Clinical Procedure Templates",
- "owner": "Administrator",
- "reference_document": "Clinical Procedure Template",
- "show_full_form": 0,
- "title": "Explore Clinical Procedure Templates",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json
deleted file mode 100644
index b2d5aef..0000000
--- a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Show Form Tour",
- "creation": "2020-05-19 11:14:33.044989",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 1,
- "is_skipped": 0,
- "modified": "2020-05-26 23:10:24.507648",
- "modified_by": "Administrator",
- "name": "Explore Healthcare Settings",
- "owner": "Administrator",
- "reference_document": "Healthcare Settings",
- "show_full_form": 0,
- "title": "Explore Healthcare Settings",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json
deleted file mode 100644
index fa4c903..0000000
--- a/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "action": "Show Form Tour",
- "creation": "2020-05-19 10:43:56.231679",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "field": "schedule",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 1,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-05-26 22:07:07.482530",
- "modified_by": "Administrator",
- "name": "Introduction to Healthcare Practitioner",
- "owner": "Administrator",
- "reference_document": "Healthcare Practitioner",
- "show_full_form": 0,
- "title": "Introduction to Healthcare Practitioner",
- "validate_action": 0
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/page/__init__.py b/erpnext/healthcare/page/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/healthcare/page/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/healthcare/page/patient_history/__init__.py b/erpnext/healthcare/page/patient_history/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/page/patient_history/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/page/patient_history/patient_history.css b/erpnext/healthcare/page/patient_history/patient_history.css
deleted file mode 100644
index 1bb5891..0000000
--- a/erpnext/healthcare/page/patient_history/patient_history.css
+++ /dev/null
@@ -1,131 +0,0 @@
-#page-medical_record .label {
- display: inline-block;
- margin-right: 7px;
-}
-
-#page-medical_record .list-row {
- border: none;
- padding: 0px;
- cursor: pointer;
-}
-
-.medical_record-label {
- max-width: 100px;
- margin-bottom: -4px;
-}
-
-.medical_record-row > * {
- z-index: -999;
-}
-
-.date-indicator {
- background:none;
- font-size:12px;
- vertical-align:middle;
- font-weight:bold;
- color:#6c7680;
-}
-.date-indicator::after {
- margin:0 -4px 0 12px;
- content:'';
- display:inline-block;
- height:8px;
- width:8px;
- border-radius:8px;
- background: #d1d8dd;
-}
-
-.date-indicator.blue {
- color: #5e64ff;
-}
-
-.div-bg-color {
- background: #fafbfc;
-}
-
-.bg-color-white {
- background: #FFFFFF;
-}
-
-.d-flex {
- display: flex;
-}
-
-.width-full {
- width: 100%;
-}
-
-.p-3 {
- padding: 16px;
-}
-
-.mt-2 {
- margin-top: 8px;
-}
-
-.mr-3 {
- margin-right: 16px;
-}
-
-.Box {
- background-color: #fff;
- border: 1px solid #d1d5da;
- border-radius: 3px;
-}
-
-.flex-column {
- flex-direction: column;
-}
-
-.avatar {
- display: inline-block;
- overflow: hidden;
- line-height: 1;
- vertical-align: middle;
- border-radius: 3px;
-}
-
-.py-3 {
- padding-top: 16px;
- padding-bottom: 16px;
-}
-
-.border-bottom {
- border-bottom: 1px #e1e4e8 solid;
-}
-
-.date-indicator.blue::after {
- background: #5e64ff;
-}
-
-.medical_record-message {
- border-left: 1px solid #d1d8dd;
- padding: 15px;
- padding-right: 30px;
-}
-
-.medical_record-date {
- padding: 15px;
- padding-right: 0px;
-}
-
-.patient-history-filter {
- margin-left: 35px;
- width: 25%;
-}
-
-#page-medical_record .plot-wrapper {
- padding: 20px 15px;
- border-bottom: 1px solid #d1d8dd;
- text-align: center;
-}
-
-#page-medical_record .plot {
- height: 140px ;
- width: 97% ;
- margin: auto;
-}
-
-#page-medical_record .list-filters {
- display: none ;
-}
diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html
deleted file mode 100644
index be486c6..0000000
--- a/erpnext/healthcare/page/patient_history/patient_history.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<div class="col-sm-12">
- <div class="col-sm-3">
- <p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p>
- <div class="patient_details" style="z-index=0"></div>
- </div>
- <div class="col-sm-9 patient_documents">
- <div class="col-sm-12">
- <div class="col-sm-12 show_chart_btns" align="center">
- </div>
- <div id="chart" class="col-sm-12 patient_vital_charts">
- </div>
- </div>
- <div class="header-separator col-sm-12 d-flex border-bottom py-3" style="display:none"></div>
- <div class="row">
- <div class="col-sm-12 d-flex">
- <div class="patient-history-filter doctype-filter"></div>
- <div class="patient-history-filter date-filter"></div>
- </div>
- </div>
- <div class="col-sm-12 patient_documents_list">
- </div>
- <div class="col-sm-12 text-center py-3">
- <a class="btn btn-sm btn-default btn-get-records" style="display:none">More..</a>
- </div>
- </div>
-</div>
\ No newline at end of file
diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js
deleted file mode 100644
index 54343aa..0000000
--- a/erpnext/healthcare/page/patient_history/patient_history.js
+++ /dev/null
@@ -1,403 +0,0 @@
-frappe.provide('frappe.patient_history');
-frappe.pages['patient_history'].on_page_load = function(wrapper) {
- let me = this;
- let page = frappe.ui.make_app_page({
- parent: wrapper,
- title: 'Patient History',
- single_column: true
- });
-
- frappe.breadcrumbs.add('Healthcare');
- let pid = '';
- page.main.html(frappe.render_template('patient_history', {}));
- page.main.find('.header-separator').hide();
-
- let patient = frappe.ui.form.make_control({
- parent: page.main.find('.patient'),
- df: {
- fieldtype: 'Link',
- options: 'Patient',
- fieldname: 'patient',
- placeholder: __('Select Patient'),
- only_select: true,
- change: function() {
- let patient_id = patient.get_value();
- if (pid != patient_id && patient_id) {
- me.start = 0;
- me.page.main.find('.patient_documents_list').html('');
- setup_filters(patient_id, me);
- get_documents(patient_id, me);
- show_patient_info(patient_id, me);
- show_patient_vital_charts(patient_id, me, 'bp', 'mmHg', 'Blood Pressure');
- }
- pid = patient_id;
- }
- },
- });
- patient.refresh();
-
- if (frappe.route_options) {
- patient.set_value(frappe.route_options.patient);
- }
-
- this.page.main.on('click', '.btn-show-chart', function() {
- let btn_show_id = $(this).attr('data-show-chart-id'), pts = $(this).attr('data-pts');
- let title = $(this).attr('data-title');
- show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title);
- });
-
- this.page.main.on('click', '.btn-more', function() {
- let doctype = $(this).attr('data-doctype'), docname = $(this).attr('data-docname');
- if (me.page.main.find('.'+docname).parent().find('.document-html').attr('data-fetched') == '1') {
- me.page.main.find('.'+docname).hide();
- me.page.main.find('.'+docname).parent().find('.document-html').show();
- } else {
- if (doctype && docname) {
- let exclude = ['patient', 'patient_name', 'patient_sex', 'encounter_date'];
- frappe.call({
- method: 'erpnext.healthcare.utils.render_doc_as_html',
- args:{
- doctype: doctype,
- docname: docname,
- exclude_fields: exclude
- },
- freeze: true,
- callback: function(r) {
- if (r.message) {
- me.page.main.find('.' + docname).hide();
-
- me.page.main.find('.' + docname).parent().find('.document-html').html(
- `${r.message.html}
- <div align='center'>
- <a class='btn octicon octicon-chevron-up btn-default btn-xs btn-less'
- data-doctype='${doctype}'
- data-docname='${docname}'>
- </a>
- </div>
- `);
-
- me.page.main.find('.' + docname).parent().find('.document-html').show();
- me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1');
- }
- }
- });
- }
- }
- });
-
- this.page.main.on('click', '.btn-less', function() {
- let docname = $(this).attr('data-docname');
- me.page.main.find('.' + docname).parent().find('.document-id').show();
- me.page.main.find('.' + docname).parent().find('.document-html').hide();
- });
- me.start = 0;
- me.page.main.on('click', '.btn-get-records', function() {
- get_documents(patient.get_value(), me);
- });
-};
-
-let setup_filters = function(patient, me) {
- $('.doctype-filter').empty();
- frappe.xcall(
- 'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes'
- ).then(document_types => {
- let doctype_filter = frappe.ui.form.make_control({
- parent: $('.doctype-filter'),
- df: {
- fieldtype: 'MultiSelectList',
- fieldname: 'document_type',
- placeholder: __('Select Document Type'),
- input_class: 'input-xs',
- change: () => {
- me.start = 0;
- me.page.main.find('.patient_documents_list').html('');
- get_documents(patient, me, doctype_filter.get_value(), date_range_field.get_value());
- },
- get_data: () => {
- return document_types.map(document_type => {
- return {
- description: document_type,
- value: document_type
- };
- });
- },
- }
- });
- doctype_filter.refresh();
-
- $('.date-filter').empty();
- let date_range_field = frappe.ui.form.make_control({
- df: {
- fieldtype: 'DateRange',
- fieldname: 'date_range',
- placeholder: __('Date Range'),
- input_class: 'input-xs',
- change: () => {
- let selected_date_range = date_range_field.get_value();
- if (selected_date_range && selected_date_range.length === 2) {
- me.start = 0;
- me.page.main.find('.patient_documents_list').html('');
- get_documents(patient, me, doctype_filter.get_value(), selected_date_range);
- }
- }
- },
- parent: $('.date-filter')
- });
- date_range_field.refresh();
- });
-};
-
-let get_documents = function(patient, me, document_types="", selected_date_range="") {
- let filters = {
- name: patient,
- start: me.start,
- page_length: 20
- };
- if (document_types)
- filters['document_types'] = document_types;
- if (selected_date_range)
- filters['date_range'] = selected_date_range;
-
- frappe.call({
- 'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed',
- args: filters,
- callback: function(r) {
- let data = r.message;
- if (data.length) {
- add_to_records(me, data);
- } else {
- me.page.main.find('.patient_documents_list').append(`
- <div class='text-muted' align='center'>
- <br><br>${__('No more records..')}<br><br>
- </div>`);
- me.page.main.find('.btn-get-records').hide();
- }
- }
- });
-};
-
-let add_to_records = function(me, data) {
- let details = "<ul class='nav nav-pills nav-stacked'>";
- let i;
- for (i=0; i<data.length; i++) {
- if (data[i].reference_doctype) {
- let label = '';
- if (data[i].subject) {
- label += "<br/>" + data[i].subject;
- }
- data[i] = add_date_separator(data[i]);
-
- if (frappe.user_info(data[i].owner).image) {
- data[i].imgsrc = frappe.utils.get_file_link(frappe.user_info(data[i].owner).image);
- } else {
- data[i].imgsrc = false;
- }
-
- let time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``;
- time_line_heading += data[i].reference_doctype + " - " +
- `<a onclick="frappe.set_route('Form', '${data[i].reference_doctype}', '${data[i].reference_name}');">
- ${data[i].reference_name}
- </a>`;
-
- details += `
- <li data-toggle='pill' class='patient_doc_menu'
- data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
- <div class='col-sm-12 d-flex border-bottom py-3'>`;
-
- if (data[i].imgsrc) {
- details += `
- <span class='mr-3'>
- <img class='avtar' src='${data[i].imgsrc}' width='32' height='32'></img>
- </span>`;
- } else {
- details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'>
- <div align='center' class='standard-image' style='background-color: #fafbfc;'>
- ${data[i].practitioner ? data[i].practitioner.charAt(0) : 'U'}
- </div>
- </span>`;
- }
-
- details += `<div class='d-flex flex-column width-full'>
- <div>
- `+time_line_heading+`
- <span>
- ${data[i].date_sep}
- </span>
- </div>
- <div class='Box p-3 mt-2'>
- <span class='${data[i].reference_name} document-id'>${label}
- <div align='center'>
- <a class='btn octicon octicon-chevron-down btn-default btn-xs btn-more'
- data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
- </a>
- </div>
- </span>
- <span class='document-html' hidden data-fetched="0">
- </span>
- </div>
- </div>
- </div>
- </li>`;
- }
- }
-
- details += '</ul>';
- me.page.main.find('.patient_documents_list').append(details);
- me.start += data.length;
-
- if (data.length === 20) {
- me.page.main.find(".btn-get-records").show();
- } else {
- me.page.main.find(".btn-get-records").hide();
- me.page.main.find(".patient_documents_list").append(`
- <div class='text-muted' align='center'>
- <br><br>${__('No more records..')}<br><br>
- </div>`);
- }
-};
-
-let add_date_separator = function(data) {
- let date = frappe.datetime.str_to_obj(data.communication_date);
- let pdate = '';
- let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
-
- if (diff < 1) {
- pdate = __('Today');
- } else if (diff < 2) {
- pdate = __('Yesterday');
- } else {
- pdate = __('on ') + frappe.datetime.global_date_format(date);
- }
- data.date_sep = pdate;
- return data;
-};
-
-let show_patient_info = function(patient, me) {
- frappe.call({
- 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
- args: {
- patient: patient
- },
- callback: function(r) {
- let data = r.message;
- let details = '';
- if (data.image) {
- details += `<div><img class='thumbnail' width=75% src='${data.image}'></div>`;
- }
-
- details += `<b> ${data.patient_name} </b><br> ${data.sex}`;
- if (data.email) details += `<br> ${data.email}`;
- if (data.mobile) details += `<br> ${data.mobile}`;
- if (data.occupation) details += `<br><br><b> ${__('Occupation')} : </b> ${data.occupation}`;
- if (data.blood_group) details += `<br><b> ${__('Blood Group')} : </b> ${data.blood_group}`;
- if (data.allergies) details += `<br><br><b> ${__('Allerigies')} : </b> ${data.allergies.replace("\n", ", ")}`;
- if (data.medication) details += `<br><b> ${__('Medication')} : </b> ${data.medication.replace("\n", ", ")}`;
- if (data.alcohol_current_use) details += `<br><br><b> ${__('Alcohol use')} : </b> ${data.alcohol_current_use}`;
- if (data.alcohol_past_use) details += `<br><b> ${__('Alcohol past use')} : </b> ${data.alcohol_past_use}`;
- if (data.tobacco_current_use) details += `<br><b> ${__('Tobacco use')} : </b> ${data.tobacco_current_use}`;
- if (data.tobacco_past_use) details += `<br><b> ${__('Tobacco past use')} : </b> ${data.tobacco_past_use}`;
- if (data.medical_history) details += `<br><br><b> ${__('Medical history')} : </b> ${data.medical_history.replace("\n", ", ")}`;
- if (data.surgical_history) details += `<br><b> ${__('Surgical history')} : </b> ${data.surgical_history.replace("\n", ", ")}`;
- if (data.surrounding_factors) details += `<br><br><b> ${__('Occupational hazards')} : </b> ${data.surrounding_factors.replace("\n", ", ")}`;
- if (data.other_risk_factors) details += `<br><b> ${__('Other risk factors')} : </b> ${data.other_risk_factors.replace("\n", ", ")}`;
- if (data.patient_details) details += `<br><br><b> ${__('More info')} : </b> ${data.patient_details.replace("\n", ", ")}`;
-
- if (details) {
- details = `<div style='padding-left:10px; font-size:13px;' align='left'>` + details + `</div>`;
- }
- me.page.main.find('.patient_details').html(details);
- }
- });
-};
-
-let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
- frappe.call({
- method: 'erpnext.healthcare.utils.get_patient_vitals',
- args:{
- patient: patient
- },
- callback: function(r) {
- if (r.message) {
- let show_chart_btns_html = `
- <div style='padding-top:10px;'>
- <a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'>
- ${__('Blood Pressure')}
- </a>
- <a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>
- ${__('Respiratory/Pulse Rate')}
- </a>
- <a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' data-pts='°C or °F' data-title='Temperature'>
- ${__('Temperature')}
- </a>
- <a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' data-pts='' data-title='BMI'>
- ${__('BMI')}
- </a>
- </div>`;
-
- me.page.main.find('.show_chart_btns').html(show_chart_btns_html);
- let data = r.message;
- let labels = [], datasets = [];
- let bp_systolic = [], bp_diastolic = [], temperature = [];
- let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = [];
-
- for (let i=0; i<data.length; i++) {
- labels.push(data[i].signs_date+'||'+data[i].signs_time);
-
- if (btn_show_id === 'bp') {
- bp_systolic.push(data[i].bp_systolic);
- bp_diastolic.push(data[i].bp_diastolic);
- }
- if (btn_show_id === 'temperature') {
- temperature.push(data[i].temperature);
- }
- if (btn_show_id === 'pulse_rate') {
- pulse.push(data[i].pulse);
- respiratory_rate.push(data[i].respiratory_rate);
- }
- if (btn_show_id === 'bmi') {
- bmi.push(data[i].bmi);
- height.push(data[i].height);
- weight.push(data[i].weight);
- }
- }
- if (btn_show_id === 'temperature') {
- datasets.push({name: 'Temperature', values: temperature, chartType: 'line'});
- }
- if (btn_show_id === 'bmi') {
- datasets.push({name: 'BMI', values: bmi, chartType: 'line'});
- datasets.push({name: 'Height', values: height, chartType: 'line'});
- datasets.push({name: 'Weight', values: weight, chartType: 'line'});
- }
- if (btn_show_id === 'bp') {
- datasets.push({name: 'BP Systolic', values: bp_systolic, chartType: 'line'});
- datasets.push({name: 'BP Diastolic', values: bp_diastolic, chartType: 'line'});
- }
- if (btn_show_id === 'pulse_rate') {
- datasets.push({name: 'Heart Rate / Pulse', values: pulse, chartType: 'line'});
- datasets.push({name: 'Respiratory Rate', values: respiratory_rate, chartType: 'line'});
- }
- new frappe.Chart('.patient_vital_charts', {
- data: {
- labels: labels,
- datasets: datasets
- },
-
- title: title,
- type: 'axis-mixed',
- height: 200,
- colors: ['purple', '#ffa3ef', 'light-blue'],
-
- tooltipOptions: {
- formatTooltipX: d => (d + '').toUpperCase(),
- formatTooltipY: d => d + ' ' + pts,
- }
- });
- me.page.main.find('.header-separator').show();
- } else {
- me.page.main.find('.patient_vital_charts').html('');
- me.page.main.find('.show_chart_btns').html('');
- me.page.main.find('.header-separator').hide();
- }
- }
- });
-};
diff --git a/erpnext/healthcare/page/patient_history/patient_history.json b/erpnext/healthcare/page/patient_history/patient_history.json
deleted file mode 100644
index b3892a4..0000000
--- a/erpnext/healthcare/page/patient_history/patient_history.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "content": null,
- "creation": "2018-08-08 17:09:13.816199",
- "docstatus": 0,
- "doctype": "Page",
- "icon": "",
- "idx": 0,
- "modified": "2018-08-08 17:09:55.969424",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "patient_history",
- "owner": "Administrator",
- "page_name": "patient_history",
- "restrict_to_domain": "Healthcare",
- "roles": [
- {
- "role": "Healthcare Administrator"
- },
- {
- "role": "Physician"
- }
- ],
- "script": null,
- "standard": "Yes",
- "style": null,
- "system_page": 0,
- "title": "Patient History"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py
deleted file mode 100644
index 4cdfd64..0000000
--- a/erpnext/healthcare/page/patient_history/patient_history.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, ESS LLP and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe.utils import cint
-from erpnext.healthcare.utils import render_docs_as_html
-
-@frappe.whitelist()
-def get_feed(name, document_types=None, date_range=None, start=0, page_length=20):
- """get feed"""
- filters = get_filters(name, document_types, date_range)
-
- result = frappe.db.get_all('Patient Medical Record',
- fields=['name', 'owner', 'communication_date',
- 'reference_doctype', 'reference_name', 'subject'],
- filters=filters,
- order_by='communication_date DESC',
- limit=cint(page_length),
- start=cint(start)
- )
-
- return result
-
-
-def get_filters(name, document_types=None, date_range=None):
- filters = {'patient': name}
- if document_types:
- document_types = json.loads(document_types)
- if len(document_types):
- filters['reference_doctype'] = ['IN', document_types]
-
- if date_range:
- try:
- date_range = json.loads(date_range)
- if date_range:
- filters['communication_date'] = ['between', [date_range[0], date_range[1]]]
- except json.decoder.JSONDecodeError:
- pass
-
- return filters
-
-
-@frappe.whitelist()
-def get_feed_for_dt(doctype, docname):
- """get feed"""
- result = frappe.db.get_all('Patient Medical Record',
- fields=['name', 'owner', 'communication_date',
- 'reference_doctype', 'reference_name', 'subject'],
- filters={
- 'reference_doctype': doctype,
- 'reference_name': docname
- },
- order_by='communication_date DESC'
- )
-
- return result
-
-
-@frappe.whitelist()
-def get_patient_history_doctypes():
- document_types = []
- settings = frappe.get_single("Patient History Settings")
-
- for entry in settings.standard_doctypes:
- document_types.append(entry.document_type)
-
- for entry in settings.custom_doctypes:
- document_types.append(entry.document_type)
-
- return document_types
diff --git a/erpnext/healthcare/page/patient_progress/__init__.py b/erpnext/healthcare/page/patient_progress/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/page/patient_progress/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.css b/erpnext/healthcare/page/patient_progress/patient_progress.css
deleted file mode 100644
index 5d85a74..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress.css
+++ /dev/null
@@ -1,165 +0,0 @@
-/* sidebar */
-
-.layout-side-section .frappe-control[data-fieldname='patient'] {
- max-width: 300px;
-}
-
-.patient-image-container {
- margin-top: 17px;
-}
-
-.patient-image {
- display: inline-block;
- width: 100%;
- height: 0;
- padding: 50% 0px;
- background-size: cover;
- background-repeat: no-repeat;
- background-position: center center;
- border-radius: 4px;
-}
-
-.patient-details {
- margin: -5px 5px;
-}
-
-.important-links {
- margin: 30px 5px;
-}
-
-.patient-name {
- font-size: 20px;
-}
-
-/* heatmap */
-
-.heatmap-container {
- height: 170px;
-}
-
-.patient-heatmap {
- width: 80%;
- display: inline-block;
-}
-
-.patient-heatmap .chart-container {
- margin-left: 30px;
-}
-
-.patient-heatmap .frappe-chart {
- margin-top: 5px;
-}
-
-.patient-heatmap .frappe-chart .chart-legend {
- display: none;
-}
-
-.heatmap-container .chart-filter {
- position: relative;
- top: 5px;
- margin-right: 10px;
-}
-
-/* percentage chart */
-
-.percentage-chart-container {
- height: 130px;
-}
-
-.percentage-chart-container .chart-filter {
- position: relative;
- top: 5px;
- margin-right: 10px;
-}
-
-.therapy-session-percentage-chart .frappe-chart {
- position: absolute;
- top: 5px;
-}
-
-/* line charts */
-
-.date-field .clearfix {
- display: none;
-}
-
-.date-field .help-box {
- display: none;
-}
-
-.date-field .frappe-control {
- margin-bottom: 0px !important;
-}
-
-.date-field .form-group {
- margin-bottom: 0px !important;
-}
-
-/* common */
-
-text.title {
- text-transform: uppercase;
- font-size: 11px;
- margin-left: 20px;
- margin-top: 20px;
- display: block;
-}
-
-.chart-filter-search {
- margin-left: 35px;
- width: 25%;
-}
-
-.chart-column-container {
- border-bottom: 1px solid #d1d8dd;
- margin: 5px 0;
-}
-
-.line-chart-container .frappe-chart {
- margin-top: -20px;
-}
-
-.line-chart-container {
- margin-bottom: 20px;
-}
-
-.chart-control {
- align-self: center;
- display: flex;
- flex-direction: row-reverse;
- margin-top: -25px;
-}
-
-.chart-control > * {
- margin-right: 10px;
-}
-
-/* mobile */
-
-@media (max-width: 991px) {
- .patient-progress-sidebar {
- display: flex;
- }
-
- .percentage-chart-container {
- border-top: 1px solid #d1d8dd;
- }
-
- .percentage-chart-container .chart-filter {
- position: relative;
- top: 12px;
- margin-right: 10px;
- }
-
- .patient-progress-sidebar .important-links {
- margin: 0;
- }
-
- .patient-progress-sidebar .patient-details {
- width: 50%;
- }
-
- .chart-filter-search {
- width: 40%;
- }
-}
diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.html b/erpnext/healthcare/page/patient_progress/patient_progress.html
deleted file mode 100644
index c20537e..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<div class="row patient-progress">
- <div class="col-md-12">
- <div class="progress-graphs">
- <div class="chart-column-container heatmap-container hidden-xs hidden-sm">
- <div class="patient-heatmap"></div>
- </div>
- <div class="chart-column-container percentage-chart-container">
- <div class="therapy-session-percentage-chart"></div>
- </div>
-
- <div class="therapy-progress">
- <div class="chart-head">
- <text class="title" text-anchor="start">Therapy Progress</text>
- <div class="chart-control pull-right"></div>
- </div>
- <div class="row">
- <div class="chart-filter-search therapy-type-search"></div>
- </div>
- <div class="col-md-12 chart-column-container line-chart-container">
- <div class="therapy-progress-line-chart">
- </div>
- </div>
- </div>
-
- <div class="assessment-results">
- <div class="chart-head">
- <text class="title" text-anchor="start">Assessment Results</text>
- <div class="chart-control pull-right"></div>
- </div>
- <div class="row">
- <div class="chart-filter-search assessment-template-search"></div>
- </div>
- <div class="col-md-12 chart-column-container line-chart-container">
- <div class="assessment-results-line-chart">
- </div>
- </div>
- </div>
-
- <div class="therapy-assessment-correlation progress-line-chart">
- <div class="chart-head">
- <text class="title" text-anchor="start">Therapy Type and Assessment Correlation</text>
- <div class="chart-control pull-right"></div>
- </div>
- <div class="row">
- <div class="chart-filter-search assessment-correlation-template-search"></div>
- </div>
- <div class="col-md-12 chart-column-container line-chart-container">
- <div class="therapy-assessment-correlation-chart">
- </div>
- </div>
- </div>
-
- <div class="assessment-parameter-progress progress-line-chart">
- <div class="chart-head">
- <text class="title" text-anchor="start">Assessment Parameter Wise Progress</text>
- <div class="chart-control pull-right"></div>
- </div>
- <div class="row">
- <div class="chart-filter-search assessment-parameter-search"></div>
- </div>
- <div class="col-md-12 line-chart-container">
- <div class="assessment-parameter-progress-chart">
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
\ No newline at end of file
diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.js b/erpnext/healthcare/page/patient_progress/patient_progress.js
deleted file mode 100644
index 2410b0c..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress.js
+++ /dev/null
@@ -1,531 +0,0 @@
-frappe.pages['patient-progress'].on_page_load = function(wrapper) {
-
- frappe.ui.make_app_page({
- parent: wrapper,
- title: __('Patient Progress')
- });
-
- let patient_progress = new PatientProgress(wrapper);
- $(wrapper).bind('show', ()=> {
- patient_progress.show();
- });
-};
-
-class PatientProgress {
-
- constructor(wrapper) {
- this.wrapper = $(wrapper);
- this.page = wrapper.page;
- this.sidebar = this.wrapper.find('.layout-side-section');
- this.main_section = this.wrapper.find('.layout-main-section');
- }
-
- show() {
- frappe.breadcrumbs.add('Healthcare');
- this.sidebar.empty();
-
- let me = this;
- let patient = frappe.ui.form.make_control({
- parent: me.sidebar,
- df: {
- fieldtype: 'Link',
- options: 'Patient',
- fieldname: 'patient',
- placeholder: __('Select Patient'),
- only_select: true,
- change: () => {
- me.patient_id = '';
- if (me.patient_id != patient.get_value() && patient.get_value()) {
- me.start = 0;
- me.patient_id = patient.get_value();
- me.make_patient_profile();
- }
- }
- }
- });
- patient.refresh();
-
- if (frappe.route_options && !this.patient) {
- patient.set_value(frappe.route_options.patient);
- this.patient_id = frappe.route_options.patient;
- }
-
- this.sidebar.find('[data-fieldname="patient"]').append('<div class="patient-info"></div>');
- }
-
- make_patient_profile() {
- this.page.set_title(__('Patient Progress'));
- this.main_section.empty().append(frappe.render_template('patient_progress'));
- this.render_patient_details();
- this.render_heatmap();
- this.render_percentage_chart('therapy_type', 'Therapy Type Distribution');
- this.create_percentage_chart_filters();
- this.show_therapy_progress();
- this.show_assessment_results();
- this.show_therapy_assessment_correlation();
- this.show_assessment_parameter_progress();
- }
-
- get_patient_info() {
- return frappe.xcall('frappe.client.get', {
- doctype: 'Patient',
- name: this.patient_id
- }).then((patient) => {
- if (patient) {
- this.patient = patient;
- }
- });
- }
-
- get_therapy_sessions_count() {
- return frappe.xcall(
- 'erpnext.healthcare.page.patient_progress.patient_progress.get_therapy_sessions_count', {
- patient: this.patient_id,
- }
- ).then(data => {
- if (data) {
- this.total_therapy_sessions = data.total_therapy_sessions;
- this.therapy_sessions_this_month = data.therapy_sessions_this_month;
- }
- });
- }
-
- render_patient_details() {
- this.get_patient_info().then(() => {
- this.get_therapy_sessions_count().then(() => {
- $('.patient-info').empty().append(frappe.render_template('patient_progress_sidebar', {
- patient_image: this.patient.image,
- patient_name: this.patient.patient_name,
- patient_gender: this.patient.sex,
- patient_mobile: this.patient.mobile,
- total_therapy_sessions: this.total_therapy_sessions,
- therapy_sessions_this_month: this.therapy_sessions_this_month
- }));
-
- this.setup_patient_profile_links();
- });
- });
- }
-
- setup_patient_profile_links() {
- this.wrapper.find('.patient-profile-link').on('click', () => {
- frappe.set_route('Form', 'Patient', this.patient_id);
- });
-
- this.wrapper.find('.therapy-plan-link').on('click', () => {
- frappe.route_options = {
- 'patient': this.patient_id,
- 'docstatus': 1
- };
- frappe.set_route('List', 'Therapy Plan');
- });
-
- this.wrapper.find('.patient-history').on('click', () => {
- frappe.route_options = {
- 'patient': this.patient_id
- };
- frappe.set_route('patient_history');
- });
- }
-
- render_heatmap() {
- this.heatmap = new frappe.Chart('.patient-heatmap', {
- type: 'heatmap',
- countLabel: 'Interactions',
- data: {},
- discreteDomains: 0
- });
- this.update_heatmap_data();
- this.create_heatmap_chart_filters();
- }
-
- update_heatmap_data(date_from) {
- frappe.xcall('erpnext.healthcare.page.patient_progress.patient_progress.get_patient_heatmap_data', {
- patient: this.patient_id,
- date: date_from || frappe.datetime.year_start(),
- }).then((data) => {
- this.heatmap.update( {dataPoints: data} );
- });
- }
-
- create_heatmap_chart_filters() {
- this.get_patient_info().then(() => {
- let filters = [
- {
- label: frappe.dashboard_utils.get_year(frappe.datetime.now_date()),
- options: frappe.dashboard_utils.get_years_since_creation(this.patient.creation),
- action: (selected_item) => {
- this.update_heatmap_data(frappe.datetime.obj_to_str(selected_item));
- }
- },
- ];
- frappe.dashboard_utils.render_chart_filters(filters, 'chart-filter', '.heatmap-container');
- });
- }
-
- render_percentage_chart(field, title) {
- frappe.xcall(
- 'erpnext.healthcare.page.patient_progress.patient_progress.get_therapy_sessions_distribution_data', {
- patient: this.patient_id,
- field: field
- }
- ).then(chart => {
- if (chart.labels.length) {
- this.percentage_chart = new frappe.Chart('.therapy-session-percentage-chart', {
- title: title,
- type: 'percentage',
- data: {
- labels: chart.labels,
- datasets: chart.datasets
- },
- truncateLegends: 1,
- barOptions: {
- height: 11,
- depth: 1
- },
- height: 160,
- maxSlices: 8,
- colors: ['#5e64ff', '#743ee2', '#ff5858', '#ffa00a', '#feef72', '#28a745', '#98d85b', '#a9a7ac'],
- });
- } else {
- this.wrapper.find('.percentage-chart-container').hide();
- }
- });
- }
-
- create_percentage_chart_filters() {
- let filters = [
- {
- label: 'Therapy Type',
- options: ['Therapy Type', 'Exercise Type'],
- fieldnames: ['therapy_type', 'exercise_type'],
- action: (selected_item, fieldname) => {
- let title = selected_item + ' Distribution';
- this.render_percentage_chart(fieldname, title);
- }
- },
- ];
- frappe.dashboard_utils.render_chart_filters(filters, 'chart-filter', '.percentage-chart-container');
- }
-
- create_time_span_filters(action_method, parent) {
- let chart_control = $(parent).find('.chart-control');
- let filters = [
- {
- label: 'Last Month',
- options: ['Select Date Range', 'Last Week', 'Last Month', 'Last Quarter', 'Last Year'],
- action: (selected_item) => {
- if (selected_item === 'Select Date Range') {
- this.render_date_range_fields(action_method, chart_control);
- } else {
- // hide date range field if visible
- let date_field = $(parent).find('.date-field');
- if (date_field.is(':visible')) {
- date_field.hide();
- }
- this[action_method](selected_item);
- }
- }
- }
- ];
- frappe.dashboard_utils.render_chart_filters(filters, 'chart-filter', chart_control, 1);
- }
-
- render_date_range_fields(action_method, parent) {
- let date_field = $(parent).find('.date-field');
-
- if (!date_field.length) {
- let date_field_wrapper = $(
- `<div class="date-field pull-right"></div>`
- ).appendTo(parent);
-
- let date_range_field = frappe.ui.form.make_control({
- df: {
- fieldtype: 'DateRange',
- fieldname: 'from_date',
- placeholder: 'Date Range',
- input_class: 'input-xs',
- reqd: 1,
- change: () => {
- let selected_date_range = date_range_field.get_value();
- if (selected_date_range && selected_date_range.length === 2) {
- this[action_method](selected_date_range);
- }
- }
- },
- parent: date_field_wrapper,
- render_input: 1
- });
- } else if (!date_field.is(':visible')) {
- date_field.show();
- }
- }
-
- show_therapy_progress() {
- let me = this;
- let therapy_type = frappe.ui.form.make_control({
- parent: $('.therapy-type-search'),
- df: {
- fieldtype: 'Link',
- options: 'Therapy Type',
- fieldname: 'therapy_type',
- placeholder: __('Select Therapy Type'),
- only_select: true,
- change: () => {
- if (me.therapy_type != therapy_type.get_value() && therapy_type.get_value()) {
- me.therapy_type = therapy_type.get_value();
- me.render_therapy_progress_chart();
- }
- }
- }
- });
- therapy_type.refresh();
- this.create_time_span_filters('render_therapy_progress_chart', '.therapy-progress');
- }
-
- render_therapy_progress_chart(time_span='Last Month') {
- if (!this.therapy_type) return;
-
- frappe.xcall(
- 'erpnext.healthcare.page.patient_progress.patient_progress.get_therapy_progress_data', {
- patient: this.patient_id,
- therapy_type: this.therapy_type,
- time_span: time_span
- }
- ).then(chart => {
- let data = {
- labels: chart.labels,
- datasets: chart.datasets
- }
- let parent = '.therapy-progress-line-chart';
- if (!chart.labels.length) {
- this.show_null_state(parent);
- } else {
- if (!this.therapy_line_chart) {
- this.therapy_line_chart = new frappe.Chart(parent, {
- type: 'axis-mixed',
- height: 250,
- data: data,
- lineOptions: {
- regionFill: 1
- },
- axisOptions: {
- xIsSeries: 1
- },
- });
- } else {
- $(parent).find('.chart-container').show();
- $(parent).find('.chart-empty-state').hide();
- this.therapy_line_chart.update(data);
- }
- }
- });
- }
-
- show_assessment_results() {
- let me = this;
- let assessment_template = frappe.ui.form.make_control({
- parent: $('.assessment-template-search'),
- df: {
- fieldtype: 'Link',
- options: 'Patient Assessment Template',
- fieldname: 'assessment_template',
- placeholder: __('Select Assessment Template'),
- only_select: true,
- change: () => {
- if (me.assessment_template != assessment_template.get_value() && assessment_template.get_value()) {
- me.assessment_template = assessment_template.get_value();
- me.render_assessment_result_chart();
- }
- }
- }
- });
- assessment_template.refresh();
- this.create_time_span_filters('render_assessment_result_chart', '.assessment-results');
- }
-
- render_assessment_result_chart(time_span='Last Month') {
- if (!this.assessment_template) return;
-
- frappe.xcall(
- 'erpnext.healthcare.page.patient_progress.patient_progress.get_patient_assessment_data', {
- patient: this.patient_id,
- assessment_template: this.assessment_template,
- time_span: time_span
- }
- ).then(chart => {
- let data = {
- labels: chart.labels,
- datasets: chart.datasets,
- yMarkers: [
- { label: 'Max Score', value: chart.max_score }
- ],
- }
- let parent = '.assessment-results-line-chart';
- if (!chart.labels.length) {
- this.show_null_state(parent);
- } else {
- if (!this.assessment_line_chart) {
- this.assessment_line_chart = new frappe.Chart(parent, {
- type: 'axis-mixed',
- height: 250,
- data: data,
- lineOptions: {
- regionFill: 1
- },
- axisOptions: {
- xIsSeries: 1
- },
- tooltipOptions: {
- formatTooltipY: d => d + __(' out of ') + chart.max_score
- }
- });
- } else {
- $(parent).find('.chart-container').show();
- $(parent).find('.chart-empty-state').hide();
- this.assessment_line_chart.update(data);
- }
- }
- });
- }
-
- show_therapy_assessment_correlation() {
- let me = this;
- let assessment = frappe.ui.form.make_control({
- parent: $('.assessment-correlation-template-search'),
- df: {
- fieldtype: 'Link',
- options: 'Patient Assessment Template',
- fieldname: 'assessment',
- placeholder: __('Select Assessment Template'),
- only_select: true,
- change: () => {
- if (me.assessment != assessment.get_value() && assessment.get_value()) {
- me.assessment = assessment.get_value();
- me.render_therapy_assessment_correlation_chart();
- }
- }
- }
- });
- assessment.refresh();
- this.create_time_span_filters('render_therapy_assessment_correlation_chart', '.therapy-assessment-correlation');
- }
-
- render_therapy_assessment_correlation_chart(time_span='Last Month') {
- if (!this.assessment) return;
-
- frappe.xcall(
- 'erpnext.healthcare.page.patient_progress.patient_progress.get_therapy_assessment_correlation_data', {
- patient: this.patient_id,
- assessment_template: this.assessment,
- time_span: time_span
- }
- ).then(chart => {
- let data = {
- labels: chart.labels,
- datasets: chart.datasets,
- yMarkers: [
- { label: 'Max Score', value: chart.max_score }
- ],
- }
- let parent = '.therapy-assessment-correlation-chart';
- if (!chart.labels.length) {
- this.show_null_state(parent);
- } else {
- if (!this.correlation_chart) {
- this.correlation_chart = new frappe.Chart(parent, {
- type: 'axis-mixed',
- height: 300,
- data: data,
- axisOptions: {
- xIsSeries: 1
- }
- });
- } else {
- $(parent).find('.chart-container').show();
- $(parent).find('.chart-empty-state').hide();
- this.correlation_chart.update(data);
- }
- }
- });
- }
-
- show_assessment_parameter_progress() {
- let me = this;
- let parameter = frappe.ui.form.make_control({
- parent: $('.assessment-parameter-search'),
- df: {
- fieldtype: 'Link',
- options: 'Patient Assessment Parameter',
- fieldname: 'assessment',
- placeholder: __('Select Assessment Parameter'),
- only_select: true,
- change: () => {
- if (me.parameter != parameter.get_value() && parameter.get_value()) {
- me.parameter = parameter.get_value();
- me.render_assessment_parameter_progress_chart();
- }
- }
- }
- });
- parameter.refresh();
- this.create_time_span_filters('render_assessment_parameter_progress_chart', '.assessment-parameter-progress');
- }
-
- render_assessment_parameter_progress_chart(time_span='Last Month') {
- if (!this.parameter) return;
-
- frappe.xcall(
- 'erpnext.healthcare.page.patient_progress.patient_progress.get_assessment_parameter_data', {
- patient: this.patient_id,
- parameter: this.parameter,
- time_span: time_span
- }
- ).then(chart => {
- let data = {
- labels: chart.labels,
- datasets: chart.datasets
- }
- let parent = '.assessment-parameter-progress-chart';
- if (!chart.labels.length) {
- this.show_null_state(parent);
- } else {
- if (!this.parameter_chart) {
- this.parameter_chart = new frappe.Chart(parent, {
- type: 'line',
- height: 250,
- data: data,
- lineOptions: {
- regionFill: 1
- },
- axisOptions: {
- xIsSeries: 1
- },
- tooltipOptions: {
- formatTooltipY: d => d + '%'
- }
- });
- } else {
- $(parent).find('.chart-container').show();
- $(parent).find('.chart-empty-state').hide();
- this.parameter_chart.update(data);
- }
- }
- });
- }
-
- show_null_state(parent) {
- let null_state = $(parent).find('.chart-empty-state');
- if (null_state.length) {
- $(null_state).show();
- } else {
- null_state = $(
- `<div class="chart-empty-state text-muted text-center" style="margin-bottom: 20px;">${__(
- "No Data..."
- )}</div>`
- );
- $(parent).append(null_state);
- }
- $(parent).find('.chart-container').hide();
- }
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.json b/erpnext/healthcare/page/patient_progress/patient_progress.json
deleted file mode 100644
index 0175cb9..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "content": null,
- "creation": "2020-06-12 15:46:23.111928",
- "docstatus": 0,
- "doctype": "Page",
- "idx": 0,
- "modified": "2020-07-23 21:45:45.540055",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "patient-progress",
- "owner": "Administrator",
- "page_name": "patient-progress",
- "restrict_to_domain": "Healthcare",
- "roles": [
- {
- "role": "Healthcare Administrator"
- },
- {
- "role": "Physician"
- },
- {
- "role": "Patient"
- },
- {
- "role": "System Manager"
- }
- ],
- "script": null,
- "standard": "Yes",
- "style": null,
- "system_page": 0,
- "title": "Patient Progress"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.py b/erpnext/healthcare/page/patient_progress/patient_progress.py
deleted file mode 100644
index a04fb2b..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress.py
+++ /dev/null
@@ -1,197 +0,0 @@
-import frappe
-from datetime import datetime
-from frappe import _
-from frappe.utils import getdate, get_timespan_date_range
-import json
-
-@frappe.whitelist()
-def get_therapy_sessions_count(patient):
- total = frappe.db.count('Therapy Session', filters={
- 'docstatus': 1,
- 'patient': patient
- })
-
- month_start = datetime.today().replace(day=1)
- this_month = frappe.db.count('Therapy Session', filters={
- 'creation': ['>', month_start],
- 'docstatus': 1,
- 'patient': patient
- })
-
- return {
- 'total_therapy_sessions': total,
- 'therapy_sessions_this_month': this_month
- }
-
-
-@frappe.whitelist()
-def get_patient_heatmap_data(patient, date):
- return dict(frappe.db.sql("""
- SELECT
- unix_timestamp(communication_date), count(*)
- FROM
- `tabPatient Medical Record`
- WHERE
- communication_date > subdate(%(date)s, interval 1 year) and
- communication_date < subdate(%(date)s, interval -1 year) and
- patient = %(patient)s
- GROUP BY communication_date
- ORDER BY communication_date asc""", {'date': date, 'patient': patient}))
-
-
-@frappe.whitelist()
-def get_therapy_sessions_distribution_data(patient, field):
- if field == 'therapy_type':
- result = frappe.db.get_all('Therapy Session',
- filters = {'patient': patient, 'docstatus': 1},
- group_by = field,
- order_by = field,
- fields = [field, 'count(*)'],
- as_list = True)
-
- elif field == 'exercise_type':
- data = frappe.db.get_all('Therapy Session', filters={
- 'docstatus': 1,
- 'patient': patient
- }, as_list=True)
- therapy_sessions = [entry[0] for entry in data]
-
- result = frappe.db.get_all('Exercise',
- filters = {
- 'parenttype': 'Therapy Session',
- 'parent': ['in', therapy_sessions],
- 'docstatus': 1
- },
- group_by = field,
- order_by = field,
- fields = [field, 'count(*)'],
- as_list = True)
-
- return {
- 'labels': [r[0] for r in result if r[0] != None],
- 'datasets': [{
- 'values': [r[1] for r in result]
- }]
- }
-
-
-@frappe.whitelist()
-def get_therapy_progress_data(patient, therapy_type, time_span):
- date_range = get_date_range(time_span)
- query_values = {'from_date': date_range[0], 'to_date': date_range[1], 'therapy_type': therapy_type, 'patient': patient}
- result = frappe.db.sql("""
- SELECT
- start_date, total_counts_targeted, total_counts_completed
- FROM
- `tabTherapy Session`
- WHERE
- start_date BETWEEN %(from_date)s AND %(to_date)s and
- docstatus = 1 and
- therapy_type = %(therapy_type)s and
- patient = %(patient)s
- ORDER BY start_date""", query_values, as_list=1)
-
- return {
- 'labels': [r[0] for r in result if r[0] != None],
- 'datasets': [
- { 'name': _('Targetted'), 'values': [r[1] for r in result if r[0] != None] },
- { 'name': _('Completed'), 'values': [r[2] for r in result if r[0] != None] }
- ]
- }
-
-@frappe.whitelist()
-def get_patient_assessment_data(patient, assessment_template, time_span):
- date_range = get_date_range(time_span)
- query_values = {'from_date': date_range[0], 'to_date': date_range[1], 'assessment_template': assessment_template, 'patient': patient}
- result = frappe.db.sql("""
- SELECT
- assessment_datetime, total_score, total_score_obtained
- FROM
- `tabPatient Assessment`
- WHERE
- DATE(assessment_datetime) BETWEEN %(from_date)s AND %(to_date)s and
- docstatus = 1 and
- assessment_template = %(assessment_template)s and
- patient = %(patient)s
- ORDER BY assessment_datetime""", query_values, as_list=1)
-
- return {
- 'labels': [getdate(r[0]) for r in result if r[0] != None],
- 'datasets': [
- { 'name': _('Score Obtained'), 'values': [r[2] for r in result if r[0] != None] }
- ],
- 'max_score': result[0][1] if result else None
- }
-
-@frappe.whitelist()
-def get_therapy_assessment_correlation_data(patient, assessment_template, time_span):
- date_range = get_date_range(time_span)
- query_values = {'from_date': date_range[0], 'to_date': date_range[1], 'assessment': assessment_template, 'patient': patient}
- result = frappe.db.sql("""
- SELECT
- therapy.therapy_type, count(*), avg(assessment.total_score_obtained), total_score
- FROM
- `tabPatient Assessment` assessment INNER JOIN `tabTherapy Session` therapy
- ON
- assessment.therapy_session = therapy.name
- WHERE
- DATE(assessment.assessment_datetime) BETWEEN %(from_date)s AND %(to_date)s and
- assessment.docstatus = 1 and
- assessment.patient = %(patient)s and
- assessment.assessment_template = %(assessment)s
- GROUP BY therapy.therapy_type
- """, query_values, as_list=1)
-
- return {
- 'labels': [r[0] for r in result if r[0] != None],
- 'datasets': [
- { 'name': _('Sessions'), 'chartType': 'bar', 'values': [r[1] for r in result if r[0] != None] },
- { 'name': _('Average Score'), 'chartType': 'line', 'values': [round(r[2], 2) for r in result if r[0] != None] }
- ],
- 'max_score': result[0][1] if result else None
- }
-
-@frappe.whitelist()
-def get_assessment_parameter_data(patient, parameter, time_span):
- date_range = get_date_range(time_span)
- query_values = {'from_date': date_range[0], 'to_date': date_range[1], 'parameter': parameter, 'patient': patient}
- results = frappe.db.sql("""
- SELECT
- assessment.assessment_datetime,
- sheet.score,
- template.scale_max
- FROM
- `tabPatient Assessment Sheet` sheet
- INNER JOIN `tabPatient Assessment` assessment
- ON sheet.parent = assessment.name
- INNER JOIN `tabPatient Assessment Template` template
- ON template.name = assessment.assessment_template
- WHERE
- DATE(assessment.assessment_datetime) BETWEEN %(from_date)s AND %(to_date)s and
- assessment.docstatus = 1 and
- sheet.parameter = %(parameter)s and
- assessment.patient = %(patient)s
- ORDER BY
- assessment.assessment_datetime asc
- """, query_values, as_list=1)
-
- score_percentages = []
- for r in results:
- if r[2] != 0 and r[0] != None:
- score = round((int(r[1]) / int(r[2])) * 100, 2)
- score_percentages.append(score)
-
- return {
- 'labels': [getdate(r[0]) for r in results if r[0] != None],
- 'datasets': [
- { 'name': _('Score'), 'values': score_percentages }
- ]
- }
-
-def get_date_range(time_span):
- try:
- time_span = json.loads(time_span)
- return time_span
- except json.decoder.JSONDecodeError:
- return get_timespan_date_range(time_span.lower())
-
diff --git a/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html b/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html
deleted file mode 100644
index cd62dd3..0000000
--- a/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<div class="patient-progress-sidebar">
- <div class="patient-image-container">
- {% if patient_image %}
- <div class="patient-image" src={{patient_image}} style="background-image: url(\'{%= patient_image %}\')"></div>
- {% endif %}
- </div>
- <div class="patient-details">
- {% if patient_name %}
- <p class="patient-name bold">{{patient_name}}</p>
- {% endif %}
- {% if patient_gender %}
- <p class="patient-gender text-muted">{%=__("Gender: ") %} {{patient_gender}}</p>
- {% endif %}
- {% if patient_mobile %}
- <p class="patient-mobile text-muted">{%=__("Contact: ") %} {{patient_mobile}}</p>
- {% endif %}
- {% if total_therapy_sessions %}
- <p class="patient-sessions text-muted">{%=__("Total Therapy Sessions: ") %} {{total_therapy_sessions}}</p>
- {% endif %}
- {% if therapy_sessions_this_month %}
- <p class="patient-sessions text-muted">{%=__("Monthly Therapy Sessions: ") %} {{therapy_sessions_this_month}}</p>
- {% endif %}
- </div>
- <div class="important-links">
- <p><a class="patient-profile-link">{%=__("Patient Profile") %}</a></p>
- <p><a class="therapy-plan-link">{%=__("Therapy Plan") %}</a></p>
- <p><a class="patient-history">{%=__("Patient History") %}</a></p>
- </div>
-</div>
\ No newline at end of file
diff --git a/erpnext/healthcare/print_format/__init__.py b/erpnext/healthcare/print_format/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/print_format/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/print_format/encounter_print/__init__.py b/erpnext/healthcare/print_format/encounter_print/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/print_format/encounter_print/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/print_format/encounter_print/encounter_print.json b/erpnext/healthcare/print_format/encounter_print/encounter_print.json
deleted file mode 100644
index ec1e0f2..0000000
--- a/erpnext/healthcare/print_format/encounter_print/encounter_print.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "align_labels_right": 0,
- "creation": "2017-04-10 14:05:53.355863",
- "custom_format": 1,
- "disabled": 0,
- "doc_type": "Patient Encounter",
- "docstatus": 0,
- "doctype": "Print Format",
- "font": "Default",
- "html": "<div >\n {% if letter_head and not no_letterhead -%}\n <div class=\"letter-head\">{{ letter_head }}</div>\n <hr>\n {% else %}\n <div align=\"right\">\n <h1>{{doc.name}}</h1>\n </div>\n {%- endif %}\n <div class=\"row section-break\">\n <div class=\"col-xs-6 column-break\">\n {% if doc.appointment %}\n\t <div class=\"row\">\n\t\t\t<div class=\"col-xs-4 text-left\">\n\t\t\t<label>Appointment</label>\n\t\t\t</div>\n\t\t\t<div class=\"col-xs-7 value\">\n\t\t\t<strong>: </strong>{{doc.appointment}}\n\t\t\t</div>\n\t\t</div>\n\t\t{%- endif -%}\n\n <div class=\"row\">\n\t\t <div class=\"col-xs-4 text-left\">\n\t\t\t <label>Patient</label>\n\t\t </div>\n {% if doc.patient %}\n\t\t <div class=\"col-xs-7 value\">\n\t\t\t <strong>: </strong>{{doc.patient}}\n\t\t </div>\n {% else %}\n <div class=\"col-xs-7 value\">\n\t\t\t <strong>: </strong><em>Patient Name</em>\n\t\t </div>\n {%- endif -%}\n\t\t</div>\n\t <div class=\"row\">\n\t\t\t<div class=\"col-xs-4 text-left\">\n\t\t\t\t<label>Age</label>\n\t\t\t</div>\n\t\t\t<div class=\"col-xs-7 value\">\n\t\t\t <strong>: </strong> {{doc.patient_age}}\n\t\t\t</div>\n\t\t</div>\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n\t\t\t\t<label>Gender</label>\n\t\t\t</div>\n\t\t\t<div class=\"col-xs-7 value\">\n\t\t\t <strong>: </strong> {{doc.patient_sex}}\n\t\t\t</div>\n </div>\n\n </div>\n <div class=\"col-xs-6 column-break\">\n\n <div class=\"row\">\n\t <div class=\"col-xs-4 text-left\">\n\t\t <label>Healthcare Practitioner</label>\n\t </div>\n {% if doc.practitioner %}\n\t <div class=\"col-xs-7 text-left value\">\n\t\t\t<strong>: </strong>{{doc.practitioner}}\n\t </div>\n {%- endif -%}\n\t</div>\n\n {% if doc.encounter_date %}\n\t <div class=\"row\">\n\t\t<div class=\"col-xs-4 text-left\">\n\t\t<label>Date</label>\n\t\t</div>\n\t\t<div class=\"col-xs-7 text-left value\">\n\t\t<strong>: </strong>{{doc.encounter_date}}\n\t\t</div>\n </div>\n\t {%- endif -%}\n {% if doc.encounter_time %}\n\t <div class=\"row\">\n\t\t<div class=\"col-xs-4 text-left\">\n\t\t<label>Time</label>\n\t\t</div>\n\t\t<div class=\"col-xs-7 text-left value\">\n\t\t<strong>: </strong>{{doc.encounter_time}}\n\t\t</div>\n </div>\n\t {%- endif -%}\n {% if doc.medical_department %}\n\t <div class=\"row\">\n\t\t<div class=\"col-xs-4 text-left\">\n\t\t<label>Department</label>\n\t\t</div>\n\t\t<div class=\"col-xs-7 text-left value\">\n\t\t<strong>: </strong>{{doc.visit_department}}\n\t\t</div>\n </div>\n {%- endif -%}\n </div>\n\n </div>\n\n</div>\n<div>\n <hr>\n {% if doc.symptoms_in_print%}\n {% if doc.symptoms %}\n Complaints:\n <strong>{{doc.symptoms}}</strong>\n \t <br>\n {%- endif -%}\n {%- endif -%}\n\n {% if doc.diagnosis_in_print%}\n {% if doc.diagnosis %}\n \t Diagnosis:\n <strong>{{doc.diagnosis}}</strong>\n <br>\n {%- endif -%}\n {%- endif -%}\n\n</div>\n\n<div>\n {% if doc.drug_prescription %}\n <br>\n Rx,\n <table class=\"table\">\n <tbody>\n <!--<tr>\n <th>Drug</th>\n <th class=\"text-left\">Dosage</th>\n <th class=\"text-left\">Period</th>\n <th>Remark</th>\n </tr>-->\n\n {%- for row in doc.drug_prescription -%}\n <tr>\n <td style=\"width: 30%;border:none;\">\n {%- if row.drug_name -%}<b>{{ row.drug_name }}</b>{%- endif -%}\n </td>\n \t<td style=\"width: 20%;text-align: left;border:none;\">\n {%- if row.dosage -%}{{ row.dosage }}{%- endif -%}\n </td>\n \t<td style=\"width: 20%;text-align: left;border:none;\">\n {%- if row.period -%}{{ row.period }}{%- endif -%}\n\t\t </td>\n <td style=\"width: 30%;text-align: left;border:none;\">\n\t\t\t <div style=\"border: 0px;\">\n {%- if row.comment -%}{{ row.comment }}{%- endif -%}\n </div>\n\t\t </td>\n </tr>\n\t {%- endfor -%}\n </tbody>\n </table>\n\n\n {%- endif -%}\n</div>\n\n\n<div>\n {% if doc.lab_test_prescription %}\n Investigations,\n <table class=\"table\">\n <tbody>\n <!--<tr>\n <th>Test</th>\n <th>Remark</th>\n </tr>-->\n\n {%- for row in doc.lab_test_prescription -%}\n <tr>\n <td style=\"width: 30%;border:none;\">\n {%- if row.lab_test_name -%}<b>{{ row.lab_test_name }}</b>{%- endif -%}\n </td>\n <td style=\"width: 30%;text-align: left;border:none;\">\n\t\t\t <div style=\"border: 0px;\">\n {%- if row.lab_test_comment -%}{{ row.lab_test_comment }}{%- endif -%}\n </div>\n\t\t </td>\n </tr>\n\n\t {%- endfor -%}\n </tbody>\n </table>\n\n\n {%- endif -%}\n</div>\n<div>\n {% if doc.encounter_comment %}\n <br>\n {{doc.encounter_comment}}\n {%- endif -%}\n</div>\n",
- "idx": 0,
- "line_breaks": 0,
- "modified": "2018-09-04 11:52:54.473702",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Encounter Print",
- "owner": "Administrator",
- "print_format_builder": 0,
- "print_format_type": "Server",
- "show_section_headings": 0,
- "standard": "Yes"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/print_format/lab_test_print/__init__.py b/erpnext/healthcare/print_format/lab_test_print/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/print_format/lab_test_print/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json b/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json
deleted file mode 100644
index f7d1676..0000000
--- a/erpnext/healthcare/print_format/lab_test_print/lab_test_print.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "align_labels_right": 0,
- "creation": "2017-04-24 15:38:45.332473",
- "custom_format": 1,
- "disabled": 0,
- "doc_type": "Lab Test",
- "docstatus": 0,
- "doctype": "Print Format",
- "font": "Default",
- "html": "<div >\n {% if letter_head and not no_letterhead -%}\n <div class=\"letter-head\">{{ letter_head }}</div>\n <hr>\n {%- endif %}\n\n {% if (doc.docstatus != 1) %}\n <div><h2 class=\"text-uppercase text-center\"><b>WORKSHEET</b></h2></div>\n\t<br/>\n\t<div class=\"row section-break\">\n <div class=\"col-xs-6 column-break\">\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Patient</label>\n </div>\n {% if doc.patient_name %}\n <div class=\"col-xs-7 value\">\n {{ doc.patient_name }}\n </div>\n {% else %}\n <div class=\"col-xs-7 value\">\n {{ doc.patient }}\n </div>\n {%- endif -%}\n </div>\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Age</label>\n </div>\n <div class=\"col-xs-7 value\">\n {{ doc.patient_age or '' }}\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Gender</label>\n </div>\n <div class=\"col-xs-7 value\">\n {{ doc.patient_sex or '' }}\n </div>\n </div>\n\n </div>\n\n <div class=\"col-xs-6 column-break\">\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Practitioner</label>\n </div>\n {% if doc.practitioner_name %}\n <div class=\"col-xs-7 text-left value\">\n {{ doc.practitioner_name }}\n </div>\n {% else %}\n\t\t\t{% if doc.referring_practitioner_name %}\n <div class=\"col-xs-7 text-left value\">\n {{ doc.referring_practitioner_name }}\n </div>\n\t\t {% endif %}\n {%- endif -%}\n </div>\n\n {% if doc.sample_date %}\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Sample Date</label>\n </div>\n <div class=\"col-xs-7 text-left value\">\n {{ doc.sample_date }}\n </div>\n </div>\n {%- endif -%}\n </div>\n </div>\n\n\t<div>\n <hr><h4 class=\"text-uppercase text-center\"><b><u>Department of {{ doc.department }}</u></b></h4>\n </div>\n\n\t<table class=\"table\">\n <tbody>\n {%- if doc.normal_test_items -%}\n <tr>\n <th>Name of Test</th>\n <th class=\"text-left\">Result</th>\n <th class=\"text-right\">Normal Range</th>\n </tr>\n\n {%- if doc.normal_test_items|length > 1 %}\n <tr><td style=\"width: 40%;\"> <b>{{ doc.lab_test_name }}</b> </td><td></td></tr>\n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n <tr>\n <td style=\"width: 40%;border:none;\">\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.lab_test_name -%}<b>{{ row.lab_test_name }}</b>\n {%- else -%}   {%- endif -%}\n {%- if row.lab_test_event -%}   {{ row.lab_test_event }}{%- endif -%}\n </td>\n\n <td style=\"width: 20%;text-align: right;border:none;\">\n {%- if row.lab_test_uom -%} {{ row.lab_test_uom }}{%- endif -%}\n </td>\n\n <td style=\"width: 30%;text-align: right;border:none;\">\n <div style=\"border: 0px;\">\n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n </div>\n </td>\n </tr>\n\n {%- endfor -%}\n {%- endif -%}\n </tbody>\n </table>\n\n\t<table class=\"table\">\n <tbody>\n {%- if doc.descriptive_test_items -%}\n <tr>\n <th>Name of Test</th>\n <th class=\"text-left\">Result</th>\n </tr>\n <tr><td style=\"width: 30%;border:none;\"> <b>{{ doc.lab_test_name }}</b> </td><td></td></tr>\n\t\t\t{% set gr_lab_test_name = {'ltname': ''} %}\n {%- for row in doc.descriptive_test_items -%}\n\t\t\t{%- if row.lab_test_name -%}\n\t\t\t{%- if row.lab_test_name != gr_lab_test_name.ltname -%}\n\t\t\t<tr>\n\t\t\t\t<td style=\"width: 30%;border:none;\">  {{ row.lab_test_name }} </td>\n\t\t\t\t<td style=\"width: 70%;text-align: left;border:none;\"></td>\n\t\t\t</tr>\n\t\t\t{% if gr_lab_test_name.update({'ltname': row.lab_test_name}) %} {% endif %}\n\t\t\t{%- endif -%}\n\t\t\t{%- endif -%}\n <tr>\n <td style=\"width: 30%;border:none;\">   {{ row.lab_test_particulars }} </td>\n <td style=\"width: 70%;text-align: left;border:none;\"></td>\n </tr>\n {%- endfor -%}\n {%- endif -%}\n </tbody>\n </table>\n <div>\n {% if doc.worksheet_instructions %}\n <hr>\n <b>Instructions</b>\n {{ doc.worksheet_instructions }}\n {%- endif -%}\n</div>\n {% elif (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"require_test_result_approval\") == '1' and doc.status != \"Approved\") %}\n <b>Lab Tests have to be Approved for Print .. !</b>\n {%- else -%}\n <div class=\"row section-break\">\n <div class=\"col-xs-6 column-break\">\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Patient</label>\n </div>\n {% if doc.patient_name %}\n <div class=\"col-xs-7 value\">\n {{ doc.patient_name }}\n </div>\n {% else %}\n <div class=\"col-xs-7 value\">\n {{ doc.patient }}\n </div>\n {%- endif -%}\n </div>\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Age</label>\n </div>\n <div class=\"col-xs-7 value\">\n {{ doc.patient_age or '' }}\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Gender</label>\n </div>\n <div class=\"col-xs-7 value\">\n {{ doc.patient_sex or '' }}\n </div>\n </div>\n\n </div>\n\n <div class=\"col-xs-6 column-break\">\n\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Practitioner</label>\n </div>\n {% if doc.practitioner_name %}\n <div class=\"col-xs-7 text-left value\">\n {{ doc.practitioner_name }}\n </div>\n\t\t{% else %}\n\t\t {% if doc.referring_practitioner_name %}\n <div class=\"col-xs-7 text-left value\">\n {{ doc.referring_practitioner_name }}\n </div>\n\t\t\t{% endif %}\n {%- endif -%}\n </div>\n\n {% if doc.sample_date %}\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Sample Date</label>\n </div>\n <div class=\"col-xs-7 text-left value\">\n {{ doc.sample_date }}\n </div>\n </div>\n {%- endif -%}\n\n {% if doc.result_date %}\n <div class=\"row\">\n <div class=\"col-xs-4 text-left\">\n <label>Result Date</label>\n </div>\n <div class=\"col-xs-7 text-left value\">\n {{ doc.result_date }}\n </div>\n </div>\n {%- endif -%}\n\n </div>\n\n </div>\n\n <div>\n <hr><h4 class=\"text-uppercase text-center\"><b><u>Department of {{ doc.department }}</u></b></h4>\n </div>\n\n\t<div>\n\t\t{% if doc.result_legend and (doc.legend_print_position == \"Top\" or doc.legend_print_position == \"Both\")%}\n\t\t<b>Result Legend:</b>\n\t\t{{ doc.result_legend }}\n\t\t{%- endif -%}\n\t</div>\n\n <table class=\"table\">\n <tbody>\n {%- if doc.normal_test_items -%}\n <tr>\n <th>Name of Test</th>\n <th class=\"text-left\">Result</th>\n <th class=\"text-right\">Normal Range</th>\n </tr>\n\n {%- if doc.normal_test_items|length > 1 %}\n <tr><td style=\"width: 40%;\"> <b>{{ doc.lab_test_name }}</b> </td><td></td></tr>\n {%- endif -%}\n\n {%- for row in doc.normal_test_items -%}\n <tr>\n <td style=\"width: 40%;border:none;\">\n {%- if doc.normal_test_items|length > 1 %}  {%- endif -%}\n {%- if row.lab_test_name -%}<b>{{ row.lab_test_name }}</b>\n {%- else -%}   {%- endif -%}\n {%- if row.lab_test_event -%}   {{ row.lab_test_event }}{%- endif -%}\n </td>\n\n <td style=\"width: 20%;text-align: left;border:none;\">\n\t\t\t\t\t{%- if row.result_value -%}\n\t\t\t\t\t\t{%- if row.bold -%}<b>{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}<u>{% endif %}\n\t\t\t\t\t\t{%- if row.italic -%}<i>{% endif %}\n {{ row.result_value }}\n {%- if row.lab_test_uom -%} {{ row.lab_test_uom }}{%- endif -%}\n\t\t\t\t\t\t{%- if row.italic -%}</i>{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}</u>{% endif %}\n\t\t\t\t\t\t{%- if row.bold -%}</b>{% endif %}\n\t\t\t\t\t{%- endif -%}\n \n\t\t\t\t\t{%- if row.secondary_uom and row.conversion_factor and row.secondary_uom_result -%}\n\t\t\t\t\t\t<br/>\n\t\t\t\t\t\t{%- if row.bold -%}<b>{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}<u>{% endif %}\n\t\t\t\t\t\t{%- if row.italic -%}<i>{% endif %}\n {{ row.secondary_uom_result }}\n  {{ row.secondary_uom }}\n\t\t\t\t\t\t{%- if row.italic -%}</i>{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}</u>{% endif %}\n\t\t\t\t\t\t{%- if row.bold -%}</b>{% endif %}\n\t\t\t\t\t\t \n\t\t\t\t\t{%- endif -%}\n </td>\n\n <td style=\"width: 30%;text-align: right;border:none;\">\n <div style=\"border: 0px;\">\n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n </div>\n </td>\n </tr>\n\n {%- endfor -%}\n {%- endif -%}\n </tbody>\n </table>\n\n <table class=\"table\">\n <tbody>\n {%- if doc.descriptive_test_items -%}\n <tr>\n <th>Name of Test</th>\n <th class=\"text-left\">Result</th>\n </tr>\n <tr><td style=\"width: 30%;border:none;\"> <b>{{ doc.lab_test_name }}</b> </td><td></td></tr>\n\t\t\t{% set gr_lab_test_name = {'ltname': ''} %}\n {%- for row in doc.descriptive_test_items -%}\n\t\t\t{%- if row.lab_test_name -%}\n\t\t\t{%- if row.lab_test_name != gr_lab_test_name.ltname -%}\n\t\t\t<tr>\n\t\t\t\t<td style=\"width: 30%;border:none;\">  {{ row.lab_test_name }} </td>\n\t\t\t\t<td style=\"width: 70%;text-align: left;border:none;\"></td>\n\t\t\t</tr>\n\t\t\t{% if gr_lab_test_name.update({'ltname': row.lab_test_name}) %} {% endif %}\n\t\t\t{%- endif -%}\n\t\t\t{%- endif -%}\n <tr>\n <td style=\"width: 30%;border:none;\">   {{ row.lab_test_particulars }} </td>\n <td style=\"width: 70%;text-align: left;border:none;\">\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n </td>\n </tr>\n {%- endfor -%}\n {%- endif -%}\n\n\t\t\t{%- if doc.organisms -%}\n\t\t\t<tr>\n\t\t\t\t<th>Organism</th>\n\t\t\t\t<th class=\"text-left\">Colony Population</th>\n\t\t\t</tr>\n\t\t\t{%- for row in doc.organisms -%}\n\t\t\t<tr>\n\t\t\t\t<td style=\"width: 30%;border:none;\"> {{ row.organism }} </td>\n\t\t\t\t<td style=\"width: 60%;text-align: left;border:none;\">\n\t\t\t\t\t{{ row.colony_population }}\n\t\t\t\t\t{% if row.colony_uom %}\n\t\t\t\t\t\t{{ row.colony_uom }}\n\t\t\t\t\t{% endif %}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t{%- endfor -%}\n\t\t\t{%- endif -%}\n\n\t\t\t{%- if doc.sensitivity_test_items -%}\n\t\t\t<tr>\n\t\t\t\t<th>Antibiotic</th>\n\t\t\t\t<th class=\"text-left\">Sensitivity</th>\n\t\t\t</tr>\n\t\t\t{%- for row in doc.sensitivity_test_items -%}\n\t\t\t<tr>\n\t\t\t\t<td style=\"width: 30%;border:none;\"> {{ row.antibiotic }} </td>\n\t\t\t\t<td style=\"width: 70%;text-align: left;border:none;\">{{ row.antibiotic_sensitivity }}</td>\n\t\t\t</tr>\n\t\t\t{%- endfor -%}\n\t\t\t{%- endif -%}\n\n </tbody>\n </table>\n <div>\n {% if doc.custom_result %}\n <br/>\n <div> {{ doc.custom_result }} </div>\n {%- endif -%}\n </div>\n\n <div>\n {% if doc.lab_test_comment %}\n <br/>\n <b>Comments</b>\n {{ doc.lab_test_comment }}\n {%- endif -%}\n </div>\n\n <div class=\"text-right\">\n {%- if (frappe.db.get_value(\"Healthcare Settings\", \"None\", \"employee_name_and_designation_in_print\") == '1') -%}\n {%- if doc.employee_name -%}\n <h6 class=\"text-uppercase\"><b>{{ doc.employee_name }}</b></h6>\n {%- endif -%}\n {%- if doc.employee_designation -%}\n <h6 class=\"text-uppercase\"><b>{{ doc.employee_designation }}</b></h6>\n {%- endif -%}\n {%- else -%}\n {%- if frappe.db.get_value(\"Healthcare Settings\", \"None\", \"custom_signature_in_print\") -%}\n <h6 ><b>{{ frappe.db.get_value(\"Healthcare Settings\", \"None\", \"custom_signature_in_print\") }}</b></h6>\n {%- endif -%}\n {%- endif -%}\n </div>\n\n <div>\n {% if doc.result_legend and (doc.legend_print_position == \"Bottom\" or doc.legend_print_position == \"Both\" or doc.legend_print_position == \"\")%}\n <hr>\n <b>Result Legend</b>\n {{ doc.result_legend }}\n {%- endif -%}\n </div>\n {%- endif -%}\n</div>",
- "idx": 0,
- "line_breaks": 0,
- "modified": "2020-07-08 15:34:28.866798",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test Print",
- "owner": "Administrator",
- "print_format_builder": 0,
- "print_format_type": "Jinja",
- "raw_printing": 0,
- "show_section_headings": 0,
- "standard": "Yes"
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/print_format/sample_id_print/__init__.py b/erpnext/healthcare/print_format/sample_id_print/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/print_format/sample_id_print/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json b/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json
deleted file mode 100644
index e99ce70..0000000
--- a/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "align_labels_left": 0,
- "creation": "2017-02-17 17:40:52.967840",
- "custom_format": 1,
- "disabled": 0,
- "doc_type": "Sample Collection",
- "docstatus": 0,
- "doctype": "Print Format",
- "font": "Default",
- "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 170%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 6in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 2in;\n\t\t}\n\t}\n</style>\n{% set column = 0 %}\n<table>\n{% for _ in range(0, doc.num_print) %}\n{% if column == 0 -%}<tr>{% endif %}\n\t<td style=\"width: 2in; height: 1.8in;\">{{doc.name}}<br>{{doc.patient}}<br>\n{% if doc.patient_age %}{{doc.patient_age}}, {% endif %} {% if doc.patient_sex %}{{doc.patient_sex}}{% endif %}<br> {% if doc.collected_time %}{{doc.collected_time}} {% endif %}<br>{% if doc.collected_by %} {{doc.collected_by}} {% endif %}</td>\n{% if column == 0 %}{% set column = column+1 %}\n{% elif column == 2%} </tr>{%- set column = 0 %}\n{% else %}{%- set column = column+1 -%}{%- endif %}\n\t\n{% endfor %}\n</table>",
- "idx": 0,
- "line_breaks": 0,
- "modified": "2017-03-30 18:09:39.537609",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Sample ID Print",
- "owner": "Administrator",
- "print_format_builder": 0,
- "print_format_type": "Server",
- "show_section_headings": 0,
- "standard": "Yes"
-}
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/__init__.py b/erpnext/healthcare/report/inpatient_medication_orders/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/report/inpatient_medication_orders/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js
deleted file mode 100644
index a10f837..0000000
--- a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports["Inpatient Medication Orders"] = {
- "filters": [
- {
- fieldname: "company",
- label: __("Company"),
- fieldtype: "Link",
- options: "Company",
- default: frappe.defaults.get_user_default("Company"),
- reqd: 1
- },
- {
- fieldname: "from_date",
- label: __("From Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
- reqd: 1
- },
- {
- fieldname: "to_date",
- label: __("To Date"),
- fieldtype: "Date",
- default: frappe.datetime.now_date(),
- reqd: 1
- },
- {
- fieldname: "patient",
- label: __("Patient"),
- fieldtype: "Link",
- options: "Patient"
- },
- {
- fieldname: "service_unit",
- label: __("Healthcare Service Unit"),
- fieldtype: "Link",
- options: "Healthcare Service Unit",
- get_query: () => {
- var company = frappe.query_report.get_filter_value('company');
- return {
- filters: {
- 'company': company,
- 'is_group': 0
- }
- }
- }
- },
- {
- fieldname: "show_completed_orders",
- label: __("Show Completed Orders"),
- fieldtype: "Check",
- default: 1
- }
- ]
-};
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json
deleted file mode 100644
index 9217fa1..0000000
--- a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "add_total_row": 0,
- "columns": [],
- "creation": "2020-11-23 17:25:58.802949",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "filters": [],
- "idx": 0,
- "is_standard": "Yes",
- "json": "{}",
- "modified": "2020-11-23 19:40:20.227591",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Medication Orders",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Inpatient Medication Order",
- "report_name": "Inpatient Medication Orders",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "System Manager"
- },
- {
- "role": "Healthcare Administrator"
- },
- {
- "role": "Nursing User"
- },
- {
- "role": "Physician"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py
deleted file mode 100644
index b907730..0000000
--- a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
-
-def execute(filters=None):
- columns = get_columns()
- data = get_data(filters)
- chart = get_chart_data(data)
-
- return columns, data, None, chart
-
-def get_columns():
- return [
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "label": "Patient",
- "options": "Patient",
- "width": 200
- },
- {
- "fieldname": "healthcare_service_unit",
- "fieldtype": "Link",
- "label": "Healthcare Service Unit",
- "options": "Healthcare Service Unit",
- "width": 150
- },
- {
- "fieldname": "drug",
- "fieldtype": "Link",
- "label": "Drug Code",
- "options": "Item",
- "width": 150
- },
- {
- "fieldname": "drug_name",
- "fieldtype": "Data",
- "label": "Drug Name",
- "width": 150
- },
- {
- "fieldname": "dosage",
- "fieldtype": "Link",
- "label": "Dosage",
- "options": "Prescription Dosage",
- "width": 80
- },
- {
- "fieldname": "dosage_form",
- "fieldtype": "Link",
- "label": "Dosage Form",
- "options": "Dosage Form",
- "width": 100
- },
- {
- "fieldname": "date",
- "fieldtype": "Date",
- "label": "Date",
- "width": 100
- },
- {
- "fieldname": "time",
- "fieldtype": "Time",
- "label": "Time",
- "width": 100
- },
- {
- "fieldname": "is_completed",
- "fieldtype": "Check",
- "label": "Is Order Completed",
- "width": 100
- },
- {
- "fieldname": "healthcare_practitioner",
- "fieldtype": "Link",
- "label": "Healthcare Practitioner",
- "options": "Healthcare Practitioner",
- "width": 200
- },
- {
- "fieldname": "inpatient_medication_entry",
- "fieldtype": "Link",
- "label": "Inpatient Medication Entry",
- "options": "Inpatient Medication Entry",
- "width": 200
- },
- {
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "label": "Inpatient Record",
- "options": "Inpatient Record",
- "width": 200
- }
- ]
-
-def get_data(filters):
- conditions, values = get_conditions(filters)
-
- data = frappe.db.sql("""
- SELECT
- parent.patient, parent.inpatient_record, parent.practitioner,
- child.drug, child.drug_name, child.dosage, child.dosage_form,
- child.date, child.time, child.is_completed, child.name
- FROM `tabInpatient Medication Order` parent
- INNER JOIN `tabInpatient Medication Order Entry` child
- ON child.parent = parent.name
- WHERE
- parent.docstatus = 1
- {conditions}
- ORDER BY date, time
- """.format(conditions=conditions), values, as_dict=1)
-
- data = get_inpatient_details(data, filters.get("service_unit"))
-
- return data
-
-def get_conditions(filters):
- conditions = ""
- values = dict()
-
- if filters.get("company"):
- conditions += " AND parent.company = %(company)s"
- values["company"] = filters.get("company")
-
- if filters.get("from_date") and filters.get("to_date"):
- conditions += " AND child.date BETWEEN %(from_date)s and %(to_date)s"
- values["from_date"] = filters.get("from_date")
- values["to_date"] = filters.get("to_date")
-
- if filters.get("patient"):
- conditions += " AND parent.patient = %(patient)s"
- values["patient"] = filters.get("patient")
-
- if not filters.get("show_completed_orders"):
- conditions += " AND child.is_completed = 0"
-
- return conditions, values
-
-
-def get_inpatient_details(data, service_unit):
- service_unit_filtered_data = []
-
- for entry in data:
- entry["healthcare_service_unit"] = get_current_healthcare_service_unit(entry.inpatient_record)
- if entry.is_completed:
- entry["inpatient_medication_entry"] = get_inpatient_medication_entry(entry.name)
-
- if service_unit and entry.healthcare_service_unit and service_unit != entry.healthcare_service_unit:
- service_unit_filtered_data.append(entry)
-
- entry.pop("name", None)
-
- for entry in service_unit_filtered_data:
- data.remove(entry)
-
- return data
-
-def get_inpatient_medication_entry(order_entry):
- return frappe.db.get_value("Inpatient Medication Entry Detail", {"against_imoe": order_entry}, "parent")
-
-def get_chart_data(data):
- if not data:
- return None
-
- labels = ["Pending", "Completed"]
- datasets = []
-
- status_wise_data = {
- "Pending": 0,
- "Completed": 0
- }
-
- for d in data:
- if d.is_completed:
- status_wise_data["Completed"] += 1
- else:
- status_wise_data["Pending"] += 1
-
- datasets.append({
- "name": "Inpatient Medication Order Status",
- "values": [status_wise_data.get("Pending"), status_wise_data.get("Completed")]
- })
-
- chart = {
- "data": {
- "labels": labels,
- "datasets": datasets
- },
- "type": "donut",
- "height": 300
- }
-
- chart["fieldtype"] = "Data"
-
- return chart
\ No newline at end of file
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py
deleted file mode 100644
index 4b461f1..0000000
--- a/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import unittest
-import frappe
-import datetime
-from frappe.utils import getdate, now_datetime
-from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
-from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
-from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
-from erpnext.healthcare.report.inpatient_medication_orders.inpatient_medication_orders import execute
-
-class TestInpatientMedicationOrders(unittest.TestCase):
- @classmethod
- def setUpClass(self):
- frappe.db.sql("delete from `tabInpatient Medication Order` where company='_Test Company'")
- frappe.db.sql("delete from `tabInpatient Medication Entry` where company='_Test Company'")
- self.patient = create_patient()
- self.ip_record = create_records(self.patient)
-
- def test_inpatient_medication_orders_report(self):
- filters = {
- 'company': '_Test Company',
- 'from_date': getdate(),
- 'to_date': getdate(),
- 'patient': '_Test IPD Patient',
- 'service_unit': 'Test Service Unit Ip Occupancy - _TC'
- }
-
- report = execute(filters)
-
- expected_data = [
- {
- 'patient': '_Test IPD Patient',
- 'inpatient_record': self.ip_record.name,
- 'practitioner': None,
- 'drug': 'Dextromethorphan',
- 'drug_name': 'Dextromethorphan',
- 'dosage': 1.0,
- 'dosage_form': 'Tablet',
- 'date': getdate(),
- 'time': datetime.timedelta(seconds=32400),
- 'is_completed': 0,
- 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
- },
- {
- 'patient': '_Test IPD Patient',
- 'inpatient_record': self.ip_record.name,
- 'practitioner': None,
- 'drug': 'Dextromethorphan',
- 'drug_name': 'Dextromethorphan',
- 'dosage': 1.0,
- 'dosage_form': 'Tablet',
- 'date': getdate(),
- 'time': datetime.timedelta(seconds=50400),
- 'is_completed': 0,
- 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
- },
- {
- 'patient': '_Test IPD Patient',
- 'inpatient_record': self.ip_record.name,
- 'practitioner': None,
- 'drug': 'Dextromethorphan',
- 'drug_name': 'Dextromethorphan',
- 'dosage': 1.0,
- 'dosage_form': 'Tablet',
- 'date': getdate(),
- 'time': datetime.timedelta(seconds=75600),
- 'is_completed': 0,
- 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
- }
- ]
-
- self.assertEqual(expected_data, report[1])
-
- filters = frappe._dict(from_date=getdate(), to_date=getdate(), from_time='', to_time='')
- ipme = create_ipme(filters)
- ipme.submit()
-
- filters = {
- 'company': '_Test Company',
- 'from_date': getdate(),
- 'to_date': getdate(),
- 'patient': '_Test IPD Patient',
- 'service_unit': 'Test Service Unit Ip Occupancy - _TC',
- 'show_completed_orders': 0
- }
-
- report = execute(filters)
- self.assertEqual(len(report[1]), 0)
-
- def tearDown(self):
- if frappe.db.get_value('Patient', self.patient, 'inpatient_record'):
- # cleanup - Discharge
- schedule_discharge(frappe.as_json({'patient': self.patient}))
- self.ip_record.reload()
- mark_invoiced_inpatient_occupancy(self.ip_record)
-
- self.ip_record.reload()
- discharge_patient(self.ip_record)
-
- for entry in frappe.get_all('Inpatient Medication Entry'):
- doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
- doc.cancel()
- doc.delete()
-
- for entry in frappe.get_all('Inpatient Medication Order'):
- doc = frappe.get_doc('Inpatient Medication Order', entry.name)
- doc.cancel()
- doc.delete()
-
-
-def create_records(patient):
- frappe.db.sql("""delete from `tabInpatient Record`""")
-
- # Admit
- ip_record = create_inpatient(patient)
- ip_record.expected_length_of_stay = 0
- ip_record.save()
- ip_record.reload()
- service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
- admit_patient(ip_record, service_unit, now_datetime())
-
- ipmo = create_ipmo(patient)
- ipmo.submit()
-
- return ip_record
diff --git a/erpnext/healthcare/report/lab_test_report/__init__.py b/erpnext/healthcare/report/lab_test_report/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/report/lab_test_report/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.js b/erpnext/healthcare/report/lab_test_report/lab_test_report.js
deleted file mode 100644
index 7754e2e..0000000
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2016, ESS
-// License: See license.txt
-
-frappe.query_reports["Lab Test Report"] = {
- "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.now_date(),
- "reqd": 1
- },
- {
- "fieldname": "company",
- "label": __("Company"),
- "fieldtype": "Link",
- "default": frappe.defaults.get_default("Company"),
- "options": "Company"
- },
- {
- "fieldname": "template",
- "label": __("Lab Test Template"),
- "fieldtype": "Link",
- "options": "Lab Test Template"
- },
- {
- "fieldname": "patient",
- "label": __("Patient"),
- "fieldtype": "Link",
- "options": "Patient"
- },
- {
- "fieldname": "department",
- "label": __("Medical Department"),
- "fieldtype": "Link",
- "options": "Medical Department"
- },
- {
- "fieldname": "status",
- "label": __("Status"),
- "fieldtype": "Select",
- "options": "\nCompleted\nApproved\nRejected"
- },
- {
- "fieldname": "invoiced",
- "label": __("Invoiced"),
- "fieldtype": "Check"
- }
- ]
-};
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.json b/erpnext/healthcare/report/lab_test_report/lab_test_report.json
deleted file mode 100644
index aeb4289..0000000
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "add_total_row": 0,
- "creation": "2013-04-23 18:15:29",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 1,
- "is_standard": "Yes",
- "modified": "2020-07-30 18:53:20.102873",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test Report",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Lab Test",
- "report_name": "Lab Test Report",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Laboratory User"
- },
- {
- "role": "Nursing User"
- },
- {
- "role": "LabTest Approver"
- },
- {
- "role": "Healthcare Administrator"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py
deleted file mode 100644
index 2e59bed..0000000
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py
+++ /dev/null
@@ -1,211 +0,0 @@
-# Copyright (c) 2016, ESS
-# License: See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import msgprint, _
-
-def execute(filters=None):
- if not filters: filters = {}
-
- data, columns = [], []
-
- columns = get_columns()
- lab_test_list = get_lab_tests(filters)
-
- if not lab_test_list:
- msgprint(_('No records found'))
- return columns, lab_test_list
-
- data = []
- for lab_test in lab_test_list:
- row = frappe._dict({
- 'test': lab_test.name,
- 'template': lab_test.template,
- 'company': lab_test.company,
- 'patient': lab_test.patient,
- 'patient_name': lab_test.patient_name,
- 'practitioner': lab_test.practitioner,
- 'employee': lab_test.employee,
- 'status': lab_test.status,
- 'invoiced': lab_test.invoiced,
- 'result_date': lab_test.result_date,
- 'department': lab_test.department
- })
- data.append(row)
-
- chart = get_chart_data(data)
- report_summary = get_report_summary(data)
- return columns, data, None, chart, report_summary
-
-
-def get_columns():
- return [
- {
- 'fieldname': 'test',
- 'label': _('Lab Test'),
- 'fieldtype': 'Link',
- 'options': 'Lab Test',
- 'width': '120'
- },
- {
- 'fieldname': 'template',
- 'label': _('Lab Test Template'),
- 'fieldtype': 'Link',
- 'options': 'Lab Test Template',
- 'width': '120'
- },
- {
- 'fieldname': 'company',
- 'label': _('Company'),
- 'fieldtype': 'Link',
- 'options': 'Company',
- 'width': '120'
- },
- {
- 'fieldname': 'patient',
- 'label': _('Patient'),
- 'fieldtype': 'Link',
- 'options': 'Patient',
- 'width': '120'
- },
- {
- 'fieldname': 'patient_name',
- 'label': _('Patient Name'),
- 'fieldtype': 'Data',
- 'width': '120'
- },
- {
- 'fieldname': 'employee',
- 'label': _('Lab Technician'),
- 'fieldtype': 'Link',
- 'options': 'Employee',
- 'width': '120'
- },
- {
- 'fieldname': 'status',
- 'label': _('Status'),
- 'fieldtype': 'Data',
- 'width': '100'
- },
- {
- 'fieldname': 'invoiced',
- 'label': _('Invoiced'),
- 'fieldtype': 'Check',
- 'width': '100'
- },
- {
- 'fieldname': 'result_date',
- 'label': _('Result Date'),
- 'fieldtype': 'Date',
- 'width': '100'
- },
- {
- 'fieldname': 'practitioner',
- 'label': _('Requesting Practitioner'),
- 'fieldtype': 'Link',
- 'options': 'Healthcare Practitioner',
- 'width': '120'
- },
- {
- 'fieldname': 'department',
- 'label': _('Medical Department'),
- 'fieldtype': 'Link',
- 'options': 'Medical Department',
- 'width': '100'
- }
- ]
-
-def get_lab_tests(filters):
- conditions = get_conditions(filters)
- data = frappe.get_all(
- doctype='Lab Test',
- fields=['name', 'template', 'company', 'patient', 'patient_name', 'practitioner', 'employee', 'status', 'invoiced', 'result_date', 'department'],
- filters=conditions,
- order_by='submitted_date desc'
- )
- return data
-
-def get_conditions(filters):
- conditions = {
- 'docstatus': ('=', 1)
- }
-
- if filters.get('from_date') and filters.get('to_date'):
- conditions['result_date'] = ('between', (filters.get('from_date'), filters.get('to_date')))
- filters.pop('from_date')
- filters.pop('to_date')
-
- for key, value in filters.items():
- if filters.get(key):
- conditions[key] = value
-
- return conditions
-
-def get_chart_data(data):
- if not data:
- return None
-
- labels = ['Completed', 'Approved', 'Rejected']
-
- status_wise_data = {
- 'Completed': 0,
- 'Approved': 0,
- 'Rejected': 0
- }
-
- datasets = []
-
- for entry in data:
- status_wise_data[entry.status] += 1
-
- datasets.append({
- 'name': 'Lab Test Status',
- 'values': [status_wise_data.get('Completed'), status_wise_data.get('Approved'), status_wise_data.get('Rejected')]
- })
-
- chart = {
- 'data': {
- 'labels': labels,
- 'datasets': datasets
- },
- 'type': 'donut',
- 'height': 300,
- }
-
- return chart
-
-
-def get_report_summary(data):
- if not data:
- return None
-
- total_lab_tests = len(data)
- invoiced_lab_tests, unbilled_lab_tests = 0, 0
-
- for entry in data:
- if entry.invoiced:
- invoiced_lab_tests += 1
- else:
- unbilled_lab_tests += 1
-
- return [
- {
- 'value': total_lab_tests,
- 'indicator': 'Blue',
- 'label': 'Total Lab Tests',
- 'datatype': 'Int',
- },
- {
- 'value': invoiced_lab_tests,
- 'indicator': 'Green',
- 'label': 'Invoiced Lab Tests',
- 'datatype': 'Int',
- },
- {
- 'value': unbilled_lab_tests,
- 'indicator': 'Red',
- 'label': 'Unbilled Lab Tests',
- 'datatype': 'Int',
- }
- ]
diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.js b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.js
deleted file mode 100644
index 18d252e..0000000
--- a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.js
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports['Patient Appointment Analytics'] = {
- "filters": [
- {
- fieldname: 'tree_type',
- label: __('Tree Type'),
- fieldtype: 'Select',
- options: ['Healthcare Practitioner', 'Medical Department'],
- default: 'Healthcare Practitioner',
- reqd: 1
- },
- {
- fieldname: 'status',
- label: __('Appointment Status'),
- fieldtype: 'Select',
- options:[
- {label: __('Scheduled'), value: 'Scheduled'},
- {label: __('Open'), value: 'Open'},
- {label: __('Closed'), value: 'Closed'},
- {label: __('Expired'), value: 'Expired'},
- {label: __('Cancelled'), value: 'Cancelled'}
- ]
- },
- {
- fieldname: 'appointment_type',
- label: __('Appointment Type'),
- fieldtype: 'Link',
- options: 'Appointment Type'
- },
- {
- fieldname: 'practitioner',
- label: __('Healthcare Practitioner'),
- fieldtype: 'Link',
- options: 'Healthcare Practitioner'
- },
- {
- fieldname: 'department',
- label: __('Medical Department'),
- fieldtype: 'Link',
- options: 'Medical Department'
- },
- {
- fieldname: 'from_date',
- 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: 'range',
- label: __('Range'),
- fieldtype: 'Select',
- options:[
- {label: __('Weekly'), value: 'Weekly'},
- {label: __('Monthly'), value: 'Monthly'},
- {label: __('Quarterly'), value: 'Quarterly'},
- {label: __('Yearly'), value: 'Yearly'}
- ],
- default: 'Monthly',
- reqd: 1
- }
- ],
- after_datatable_render: function(datatable_obj) {
- $(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click();
- },
- get_datatable_options(options) {
- return Object.assign(options, {
- checkboxColumn: true,
- events: {
- onCheckRow: function(data) {
- row_name = data[2].content;
- length = data.length;
-
- 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 found = false;
- for (let i=0; i < new_datasets.length;i++) {
- if (new_datasets[i].name == row_name) {
- found = true;
- new_datasets.splice(i,1);
- break;
- }
- }
-
- if (!found) {
- new_datasets.push(entry);
- }
-
- let new_data = {
- labels: raw_data.labels,
- datasets: new_datasets
- }
-
- setTimeout(() => {
- frappe.query_report.chart.update(new_data)
- }, 500)
-
-
- setTimeout(() => {
- frappe.query_report.chart.draw(true);
- }, 1000)
-
- frappe.query_report.raw_chart_data = new_data;
- },
- }
- })
- },
-};
diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.json b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.json
deleted file mode 100644
index 64750c0..0000000
--- a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "add_total_row": 1,
- "creation": "2020-03-02 15:13:16.273493",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2020-03-02 15:13:16.273493",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Patient Appointment Analytics",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Patient Appointment",
- "report_name": "Patient Appointment Analytics",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Healthcare Administrator"
- },
- {
- "role": "LabTest Approver"
- },
- {
- "role": "Physician"
- },
- {
- "role": "Nursing User"
- },
- {
- "role": "Laboratory User"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py
deleted file mode 100644
index 9c35dbb..0000000
--- a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py
+++ /dev/null
@@ -1,194 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import getdate, flt, add_to_date, add_days
-from frappe import _ , scrub
-from six import iteritems
-from erpnext.accounts.utils import get_fiscal_year
-
-def execute(filters=None):
- return Analytics(filters).run()
-
-class Analytics(object):
- def __init__(self, filters=None):
- """Patient Appointment Analytics Report."""
- self.filters = frappe._dict(filters or {})
- self.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
- self.get_period_date_ranges()
-
- def run(self):
- self.get_columns()
- self.get_data()
- self.get_chart_data()
-
- return self.columns, self.data, None, self.chart
-
- def get_period_date_ranges(self):
- from dateutil.relativedelta import relativedelta, MO
- 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)
-
- 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]
- 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':
- period_end_date = add_days(from_date, 6)
- else:
- period_end_date = add_to_date(from_date, months=increment, days=-1)
-
- if period_end_date > to_date:
- period_end_date = to_date
-
- self.periodic_daterange.append(period_end_date)
-
- from_date = add_days(period_end_date, 1)
- if period_end_date == to_date:
- break
-
- def get_columns(self):
- self.columns = []
-
- if self.filters.tree_type == 'Healthcare Practitioner':
- self.columns.append({
- 'label': _('Healthcare Practitioner'),
- 'options': 'Healthcare Practitioner',
- 'fieldname': 'practitioner',
- 'fieldtype': 'Link',
- 'width': 200
- })
-
- elif self.filters.tree_type == 'Medical Department':
- self.columns.append({
- 'label': _('Medical Department'),
- 'fieldname': 'department',
- 'fieldtype': 'Link',
- 'options': 'Medical Department',
- 'width': 150
- })
-
- 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': _('Total'),
- 'fieldname': 'total',
- 'fieldtype': 'Int',
- 'width': 120
- })
-
- def get_data(self):
- if self.filters.tree_type == 'Healthcare Practitioner':
- self.get_appointments_based_on_healthcare_practitioner()
- self.get_rows()
-
- elif self.filters.tree_type == 'Medical Department':
- self.get_appointments_based_on_medical_department()
- self.get_rows()
-
- def get_period(self, appointment_date):
- if self.filters.range == 'Weekly':
- period = 'Week ' + str(appointment_date.isocalendar()[1])
- elif self.filters.range == 'Monthly':
- period = str(self.months[appointment_date.month - 1])
- elif self.filters.range == 'Quarterly':
- period = 'Quarter ' + str(((appointment_date.month - 1) // 3) + 1)
- else:
- year = get_fiscal_year(appointment_date, company=self.filters.company)
- period = str(year[0])
-
- if getdate(self.filters.from_date).year != getdate(self.filters.to_date).year:
- period += ' ' + str(appointment_date.year)
-
- return period
-
- def get_appointments_based_on_healthcare_practitioner(self):
- filters = self.get_common_filters()
-
- self.entries = frappe.db.get_all('Patient Appointment',
- fields=['appointment_date', 'name', 'patient', 'practitioner'],
- filters=filters
- )
-
- def get_appointments_based_on_medical_department(self):
- filters = self.get_common_filters()
- if not filters.get('department'):
- filters['department'] = ('!=', '')
-
- self.entries = frappe.db.get_all('Patient Appointment',
- fields=['appointment_date', 'name', 'patient', 'practitioner', 'department'],
- filters=filters
- )
-
- def get_common_filters(self):
- filters = {}
- filters['appointment_date'] = ('between', [self.filters.from_date, self.filters.to_date])
- for entry in ['appointment_type', 'practitioner', 'department', 'status']:
- if self.filters.get(entry):
- filters[entry] = self.filters.get(entry)
-
- return filters
-
- def get_rows(self):
- self.data = []
- self.get_periodic_data()
-
- for entity, period_data in iteritems(self.appointment_periodic_data):
- if self.filters.tree_type == 'Healthcare Practitioner':
- row = {'practitioner': entity}
- elif self.filters.tree_type == 'Medical Department':
- row = {'department': entity}
-
- total = 0
- for end_date in self.periodic_daterange:
- period = self.get_period(end_date)
- amount = flt(period_data.get(period, 0.0))
- row[scrub(period)] = amount
- total += amount
-
- row['total'] = total
-
- self.data.append(row)
-
- def get_periodic_data(self):
- self.appointment_periodic_data = frappe._dict()
-
- for d in self.entries:
- period = self.get_period(d.get('appointment_date'))
- if self.filters.tree_type == 'Healthcare Practitioner':
- self.appointment_periodic_data.setdefault(d.practitioner, frappe._dict()).setdefault(period, 0.0)
- self.appointment_periodic_data[d.practitioner][period] += 1
-
- elif self.filters.tree_type == 'Medical Department':
- self.appointment_periodic_data.setdefault(d.department, frappe._dict()).setdefault(period, 0.0)
- self.appointment_periodic_data[d.department][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"
- }
\ No newline at end of file
diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py
deleted file mode 100644
index bf4df7e..0000000
--- a/erpnext/healthcare/setup.py
+++ /dev/null
@@ -1,295 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from frappe import _
-from erpnext.setup.utils import insert_record
-
-def setup_healthcare():
- if frappe.db.exists('Medical Department', 'Cardiology'):
- # already setup
- return
- create_medical_departments()
- create_antibiotics()
- create_lab_test_uom()
- create_duration()
- create_dosage()
- create_healthcare_item_groups()
- create_sensitivity()
- add_healthcare_service_unit_tree_root()
- setup_patient_history_settings()
-
-def create_medical_departments():
- departments = [
- "Accident And Emergency Care" ,"Anaesthetics", "Biochemistry", "Cardiology", "Dermatology",
- "Diagnostic Imaging", "ENT", "Gastroenterology", "General Surgery", "Gynaecology",
- "Haematology", "Maternity", "Microbiology", "Nephrology", "Neurology", "Oncology",
- "Orthopaedics", "Pathology", "Physiotherapy", "Rheumatology", "Serology", "Urology"
- ]
- for department in departments:
- mediacal_department = frappe.new_doc("Medical Department")
- mediacal_department.department = _(department)
- try:
- mediacal_department.save()
- except frappe.DuplicateEntryError:
- pass
-
-def create_antibiotics():
- abt = [
- "Amoxicillin", "Ampicillin", "Bacampicillin", "Carbenicillin", "Cloxacillin", "Dicloxacillin",
- "Flucloxacillin", "Mezlocillin", "Nafcillin", "Oxacillin", "Penicillin G", "Penicillin V",
- "Piperacillin", "Pivampicillin", "Pivmecillinam", "Ticarcillin", "Cefacetrile (cephacetrile)",
- "Cefadroxil (cefadroxyl)", "Cefalexin (cephalexin)", "Cefaloglycin (cephaloglycin)",
- "Cefalonium (cephalonium)", "Cefaloridine (cephaloradine)", "Cefalotin (cephalothin)",
- "Cefapirin (cephapirin)", "Cefatrizine", "Cefazaflur", "Cefazedone", "Cefazolin (cephazolin)",
- "Cefradine (cephradine)", "Cefroxadine", "Ceftezole", "Cefaclor", "Cefamandole", "Cefmetazole",
- "Cefonicid", "Cefotetan", "Cefoxitin", "Cefprozil (cefproxil)", "Cefuroxime", "Cefuzonam",
- "Cefcapene", "Cefdaloxime", "Cefdinir", "Cefditoren", "Cefetamet", "Cefixime", "Cefmenoxime",
- "Cefodizime", "Cefotaxime", "Cefpimizole", "Cefpodoxime", "Cefteram", "Ceftibuten", "Ceftiofur",
- "Ceftiolene", "Ceftizoxime", "Ceftriaxone", "Cefoperazone", "Ceftazidime", "Cefclidine", "Cefepime",
- "Cefluprenam", "Cefoselis", "Cefozopran", "Cefpirome", "Cefquinome", "Ceftobiprole", "Ceftaroline",
- "Cefaclomezine","Cefaloram", "Cefaparole", "Cefcanel", "Cefedrolor", "Cefempidone", "Cefetrizole",
- "Cefivitril", "Cefmatilen", "Cefmepidium", "Cefovecin", "Cefoxazole", "Cefrotil", "Cefsumide",
- "Cefuracetime", "Ceftioxide", "Ceftazidime/Avibactam", "Ceftolozane/Tazobactam", "Aztreonam",
- "Imipenem", "Imipenem/cilastatin", "Doripenem", "Meropenem", "Ertapenem", "Azithromycin",
- "Erythromycin", "Clarithromycin", "Dirithromycin", "Roxithromycin", "Telithromycin", "Clindamycin",
- "Lincomycin", "Pristinamycin", "Quinupristin/dalfopristin", "Amikacin", "Gentamicin", "Kanamycin",
- "Neomycin", "Netilmicin", "Paromomycin", "Streptomycin", "Tobramycin", "Flumequine", "Nalidixic acid",
- "Oxolinic acid", "Piromidic acid", "Pipemidic acid", "Rosoxacin", "Ciprofloxacin", "Enoxacin",
- "Lomefloxacin", "Nadifloxacin", "Norfloxacin", "Ofloxacin", "Pefloxacin", "Rufloxacin", "Balofloxacin",
- "Gatifloxacin", "Grepafloxacin", "Levofloxacin", "Moxifloxacin", "Pazufloxacin", "Sparfloxacin",
- "Temafloxacin", "Tosufloxacin", "Besifloxacin", "Clinafloxacin", "Gemifloxacin",
- "Sitafloxacin", "Trovafloxacin", "Prulifloxacin", "Sulfamethizole", "Sulfamethoxazole",
- "Sulfisoxazole", "Trimethoprim-Sulfamethoxazole", "Demeclocycline", "Doxycycline", "Minocycline",
- "Oxytetracycline", "Tetracycline", "Tigecycline", "Chloramphenicol", "Metronidazole",
- "Tinidazole", "Nitrofurantoin", "Vancomycin", "Teicoplanin", "Telavancin", "Linezolid",
- "Cycloserine 2", "Rifampin", "Rifabutin", "Rifapentine", "Rifalazil", "Bacitracin", "Polymyxin B",
- "Viomycin", "Capreomycin"
- ]
-
- for a in abt:
- antibiotic = frappe.new_doc("Antibiotic")
- antibiotic.antibiotic_name = a
- try:
- antibiotic.save()
- except frappe.DuplicateEntryError:
- pass
-
-def create_lab_test_uom():
- records = [
- {"doctype": "Lab Test UOM", "name": "umol/L", "lab_test_uom": "umol/L", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "mg/L", "lab_test_uom": "mg/L", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "mg / dl", "lab_test_uom": "mg / dl", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "pg / ml", "lab_test_uom": "pg / ml", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "U/ml", "lab_test_uom": "U/ml", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "/HPF", "lab_test_uom": "/HPF", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "Million Cells / cumm", "lab_test_uom": "Million Cells / cumm", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "Lakhs Cells / cumm", "lab_test_uom": "Lakhs Cells / cumm", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "U / L", "lab_test_uom": "U / L", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "g / L", "lab_test_uom": "g / L", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "IU / ml", "lab_test_uom": "IU / ml", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "gm %", "lab_test_uom": "gm %", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "Microgram", "lab_test_uom": "Microgram", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "Micron", "lab_test_uom": "Micron", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "Cells / cumm", "lab_test_uom": "Cells / cumm", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "%", "lab_test_uom": "%", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "mm / dl", "lab_test_uom": "mm / dl", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "mm / hr", "lab_test_uom": "mm / hr", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "ulU / ml", "lab_test_uom": "ulU / ml", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "ng / ml", "lab_test_uom": "ng / ml", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "ng / dl", "lab_test_uom": "ng / dl", "uom_description": None },
- {"doctype": "Lab Test UOM", "name": "ug / dl", "lab_test_uom": "ug / dl", "uom_description": None }
- ]
-
- insert_record(records)
-
-def create_duration():
- records = [
- {"doctype": "Prescription Duration", "name": "3 Month", "number": "3", "period": "Month" },
- {"doctype": "Prescription Duration", "name": "2 Month", "number": "2", "period": "Month" },
- {"doctype": "Prescription Duration", "name": "1 Month", "number": "1", "period": "Month" },
- {"doctype": "Prescription Duration", "name": "12 Hour", "number": "12", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "11 Hour", "number": "11", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "10 Hour", "number": "10", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "9 Hour", "number": "9", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "8 Hour", "number": "8", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "7 Hour", "number": "7", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "6 Hour", "number": "6", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "5 Hour", "number": "5", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "4 Hour", "number": "4", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "3 Hour", "number": "3", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "2 Hour", "number": "2", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "1 Hour", "number": "1", "period": "Hour" },
- {"doctype": "Prescription Duration", "name": "5 Week", "number": "5", "period": "Week" },
- {"doctype": "Prescription Duration", "name": "4 Week", "number": "4", "period": "Week" },
- {"doctype": "Prescription Duration", "name": "3 Week", "number": "3", "period": "Week" },
- {"doctype": "Prescription Duration", "name": "2 Week", "number": "2", "period": "Week" },
- {"doctype": "Prescription Duration", "name": "1 Week", "number": "1", "period": "Week" },
- {"doctype": "Prescription Duration", "name": "6 Day", "number": "6", "period": "Day" },
- {"doctype": "Prescription Duration", "name": "5 Day", "number": "5", "period": "Day" },
- {"doctype": "Prescription Duration", "name": "4 Day", "number": "4", "period": "Day" },
- {"doctype": "Prescription Duration", "name": "3 Day", "number": "3", "period": "Day" },
- {"doctype": "Prescription Duration", "name": "2 Day", "number": "2", "period": "Day" },
- {"doctype": "Prescription Duration", "name": "1 Day", "number": "1", "period": "Day" }
- ]
- insert_record(records)
-
-def create_dosage():
- records = [
- {"doctype": "Prescription Dosage", "name": "1-1-1-1", "dosage": "1-1-1-1","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "13:00:00"},{"strength": "1.0","strength_time": "17:00:00"},{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "0-0-1", "dosage": "0-0-1","dosage_strength":
- [{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "1-0-0", "dosage": "1-0-0","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "0-1-0", "dosage": "0-1-0","dosage_strength":
- [{"strength": "1.0","strength_time": "14:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "1-1-1", "dosage": "1-1-1","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "14:00:00"},{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "1-0-1", "dosage": "1-0-1","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "Once Bedtime", "dosage": "Once Bedtime","dosage_strength":
- [{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "5 times a day", "dosage": "5 times a day","dosage_strength":
- [{"strength": "1.0","strength_time": "5:00:00"}, {"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "13:00:00"},{"strength": "1.0","strength_time": "17:00:00"},{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "QID", "dosage": "QID","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "13:00:00"},{"strength": "1.0","strength_time": "17:00:00"},{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "TID", "dosage": "TID","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "14:00:00"},{"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "BID", "dosage": "BID","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}, {"strength": "1.0","strength_time": "21:00:00"}]
- },
- {"doctype": "Prescription Dosage", "name": "Once Daily", "dosage": "Once Daily","dosage_strength":
- [{"strength": "1.0","strength_time": "9:00:00"}]
- }
- ]
- insert_record(records)
-
-def create_healthcare_item_groups():
- records = [
- {'doctype': 'Item Group', 'item_group_name': _('Laboratory'),
- 'is_group': 0, 'parent_item_group': _('All Item Groups') },
- {'doctype': 'Item Group', 'item_group_name': _('Drug'),
- 'is_group': 0, 'parent_item_group': _('All Item Groups') }
- ]
- insert_record(records)
-
-def create_sensitivity():
- records = [
- {"doctype": "Sensitivity", "sensitivity": _("Low Sensitivity")},
- {"doctype": "Sensitivity", "sensitivity": _("High Sensitivity")},
- {"doctype": "Sensitivity", "sensitivity": _("Moderate Sensitivity")},
- {"doctype": "Sensitivity", "sensitivity": _("Susceptible")},
- {"doctype": "Sensitivity", "sensitivity": _("Resistant")},
- {"doctype": "Sensitivity", "sensitivity": _("Intermediate")}
- ]
- insert_record(records)
-
-def add_healthcare_service_unit_tree_root():
- record = [
- {
- "doctype": "Healthcare Service Unit",
- "healthcare_service_unit_name": "All Healthcare Service Units",
- "is_group": 1,
- "company": get_company()
- }
- ]
- insert_record(record)
-
-def get_company():
- company = frappe.defaults.get_defaults().company
- if company:
- return company
- else:
- company = frappe.get_list("Company", limit=1)
- if company:
- return company[0].name
- return None
-
-def setup_patient_history_settings():
- import json
-
- settings = frappe.get_single('Patient History Settings')
- configuration = get_patient_history_config()
- for dt, config in configuration.items():
- settings.append("standard_doctypes", {
- "document_type": dt,
- "date_fieldname": config[0],
- "selected_fields": json.dumps(config[1])
- })
- settings.save()
-
-def get_patient_history_config():
- return {
- "Patient Encounter": ("encounter_date", [
- {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
- {"label": "Symptoms", "fieldname": "symptoms", "fieldtype": "Table Multiselect"},
- {"label": "Diagnosis", "fieldname": "diagnosis", "fieldtype": "Table Multiselect"},
- {"label": "Drug Prescription", "fieldname": "drug_prescription", "fieldtype": "Table"},
- {"label": "Lab Tests", "fieldname": "lab_test_prescription", "fieldtype": "Table"},
- {"label": "Clinical Procedures", "fieldname": "procedure_prescription", "fieldtype": "Table"},
- {"label": "Therapies", "fieldname": "therapies", "fieldtype": "Table"},
- {"label": "Review Details", "fieldname": "encounter_comment", "fieldtype": "Small Text"}
- ]),
- "Clinical Procedure": ("start_date", [
- {"label": "Procedure Template", "fieldname": "procedure_template", "fieldtype": "Link"},
- {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
- {"label": "Notes", "fieldname": "notes", "fieldtype": "Small Text"},
- {"label": "Service Unit", "fieldname": "service_unit", "fieldtype": "Healthcare Service Unit"},
- {"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"},
- {"label": "Sample", "fieldname": "sample", "fieldtype": "Link"}
- ]),
- "Lab Test": ("result_date", [
- {"label": "Test Template", "fieldname": "template", "fieldtype": "Link"},
- {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
- {"label": "Test Name", "fieldname": "lab_test_name", "fieldtype": "Data"},
- {"label": "Lab Technician Name", "fieldname": "employee_name", "fieldtype": "Data"},
- {"label": "Sample ID", "fieldname": "sample", "fieldtype": "Link"},
- {"label": "Normal Test Result", "fieldname": "normal_test_items", "fieldtype": "Table"},
- {"label": "Descriptive Test Result", "fieldname": "descriptive_test_items", "fieldtype": "Table"},
- {"label": "Organism Test Result", "fieldname": "organism_test_items", "fieldtype": "Table"},
- {"label": "Sensitivity Test Result", "fieldname": "sensitivity_test_items", "fieldtype": "Table"},
- {"label": "Comments", "fieldname": "lab_test_comment", "fieldtype": "Table"}
- ]),
- "Therapy Session": ("start_date", [
- {"label": "Therapy Type", "fieldname": "therapy_type", "fieldtype": "Link"},
- {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
- {"label": "Therapy Plan", "fieldname": "therapy_plan", "fieldtype": "Link"},
- {"label": "Duration", "fieldname": "duration", "fieldtype": "Int"},
- {"label": "Location", "fieldname": "location", "fieldtype": "Link"},
- {"label": "Healthcare Service Unit", "fieldname": "service_unit", "fieldtype": "Link"},
- {"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"},
- {"label": "Exercises", "fieldname": "exercises", "fieldtype": "Table"},
- {"label": "Total Counts Targeted", "fieldname": "total_counts_targeted", "fieldtype": "Int"},
- {"label": "Total Counts Completed", "fieldname": "total_counts_completed", "fieldtype": "Int"}
- ]),
- "Vital Signs": ("signs_date", [
- {"label": "Body Temperature", "fieldname": "temperature", "fieldtype": "Data"},
- {"label": "Heart Rate / Pulse", "fieldname": "pulse", "fieldtype": "Data"},
- {"label": "Respiratory rate", "fieldname": "respiratory_rate", "fieldtype": "Data"},
- {"label": "Tongue", "fieldname": "tongue", "fieldtype": "Select"},
- {"label": "Abdomen", "fieldname": "abdomen", "fieldtype": "Select"},
- {"label": "Reflexes", "fieldname": "reflexes", "fieldtype": "Select"},
- {"label": "Blood Pressure", "fieldname": "bp", "fieldtype": "Data"},
- {"label": "Notes", "fieldname": "vital_signs_note", "fieldtype": "Small Text"},
- {"label": "Height (In Meter)", "fieldname": "height", "fieldtype": "Float"},
- {"label": "Weight (In Kilogram)", "fieldname": "weight", "fieldtype": "Float"},
- {"label": "BMI", "fieldname": "bmi", "fieldtype": "Float"}
- ]),
- "Inpatient Medication Order": ("start_date", [
- {"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
- {"label": "Start Date", "fieldname": "start_date", "fieldtype": "Date"},
- {"label": "End Date", "fieldname": "end_date", "fieldtype": "Date"},
- {"label": "Medication Orders", "fieldname": "medication_orders", "fieldtype": "Table"},
- {"label": "Total Orders", "fieldname": "total_orders", "fieldtype": "Float"}
- ])
- }
\ No newline at end of file
diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py
deleted file mode 100644
index d3d22c8..0000000
--- a/erpnext/healthcare/utils.py
+++ /dev/null
@@ -1,719 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, earthians and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import math
-import frappe
-import json
-from frappe import _
-from frappe.utils.formatters import format_value
-from frappe.utils import time_diff_in_hours, rounded
-from six import string_types
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
-from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
-from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
-
-@frappe.whitelist()
-def get_healthcare_services_to_invoice(patient, company):
- patient = frappe.get_doc('Patient', patient)
- items_to_invoice = []
- if patient:
- validate_customer_created(patient)
- # Customer validated, build a list of billable services
- items_to_invoice += get_appointments_to_invoice(patient, company)
- items_to_invoice += get_encounters_to_invoice(patient, company)
- items_to_invoice += get_lab_tests_to_invoice(patient, company)
- items_to_invoice += get_clinical_procedures_to_invoice(patient, company)
- items_to_invoice += get_inpatient_services_to_invoice(patient, company)
- items_to_invoice += get_therapy_plans_to_invoice(patient, company)
- items_to_invoice += get_therapy_sessions_to_invoice(patient, company)
-
- return items_to_invoice
-
-
-def validate_customer_created(patient):
- if not frappe.db.get_value('Patient', patient.name, 'customer'):
- msg = _("Please set a Customer linked to the Patient")
- msg += " <b><a href='/app/Form/Patient/{0}'>{0}</a></b>".format(patient.name)
- frappe.throw(msg, title=_('Customer Not Found'))
-
-
-def get_appointments_to_invoice(patient, company):
- appointments_to_invoice = []
- patient_appointments = frappe.get_list(
- 'Patient Appointment',
- fields = '*',
- filters = {'patient': patient.name, 'company': company, 'invoiced': 0, 'status': ['not in', 'Cancelled']},
- order_by = 'appointment_date'
- )
-
- for appointment in patient_appointments:
- # Procedure Appointments
- if appointment.procedure_template:
- if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'):
- appointments_to_invoice.append({
- 'reference_type': 'Patient Appointment',
- 'reference_name': appointment.name,
- 'service': appointment.procedure_template
- })
- # Consultation Appointments, should check fee validity
- else:
- if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \
- frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}):
- continue # Skip invoicing, fee validty present
- practitioner_charge = 0
- income_account = None
- service_item = None
- if appointment.practitioner:
- details = get_service_item_and_practitioner_charge(appointment)
- service_item = details.get('service_item')
- practitioner_charge = details.get('practitioner_charge')
- income_account = get_income_account(appointment.practitioner, appointment.company)
- appointments_to_invoice.append({
- 'reference_type': 'Patient Appointment',
- 'reference_name': appointment.name,
- 'service': service_item,
- 'rate': practitioner_charge,
- 'income_account': income_account
- })
-
- return appointments_to_invoice
-
-
-def get_encounters_to_invoice(patient, company):
- if not isinstance(patient, str):
- patient = patient.name
- encounters_to_invoice = []
- encounters = frappe.get_list(
- 'Patient Encounter',
- fields=['*'],
- filters={'patient': patient, 'company': company, 'invoiced': False, 'docstatus': 1}
- )
- if encounters:
- for encounter in encounters:
- if not encounter.appointment:
- practitioner_charge = 0
- income_account = None
- service_item = None
- if encounter.practitioner:
- if encounter.inpatient_record and \
- frappe.db.get_single_value('Healthcare Settings', 'do_not_bill_inpatient_encounters'):
- continue
-
- details = get_service_item_and_practitioner_charge(encounter)
- service_item = details.get('service_item')
- practitioner_charge = details.get('practitioner_charge')
- income_account = get_income_account(encounter.practitioner, encounter.company)
-
- encounters_to_invoice.append({
- 'reference_type': 'Patient Encounter',
- 'reference_name': encounter.name,
- 'service': service_item,
- 'rate': practitioner_charge,
- 'income_account': income_account
- })
-
- return encounters_to_invoice
-
-
-def get_lab_tests_to_invoice(patient, company):
- lab_tests_to_invoice = []
- lab_tests = frappe.get_list(
- 'Lab Test',
- fields=['name', 'template'],
- filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
- )
- for lab_test in lab_tests:
- item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.template, ['item', 'is_billable'])
- if is_billable:
- lab_tests_to_invoice.append({
- 'reference_type': 'Lab Test',
- 'reference_name': lab_test.name,
- 'service': item
- })
-
- lab_prescriptions = frappe.db.sql(
- '''
- SELECT
- lp.name, lp.lab_test_code
- FROM
- `tabPatient Encounter` et, `tabLab Prescription` lp
- WHERE
- et.patient=%s
- and lp.parent=et.name
- and lp.lab_test_created=0
- and lp.invoiced=0
- ''', (patient.name), as_dict=1)
-
- for prescription in lab_prescriptions:
- item, is_billable = frappe.get_cached_value('Lab Test Template', prescription.lab_test_code, ['item', 'is_billable'])
- if prescription.lab_test_code and is_billable:
- lab_tests_to_invoice.append({
- 'reference_type': 'Lab Prescription',
- 'reference_name': prescription.name,
- 'service': item
- })
-
- return lab_tests_to_invoice
-
-
-def get_clinical_procedures_to_invoice(patient, company):
- clinical_procedures_to_invoice = []
- procedures = frappe.get_list(
- 'Clinical Procedure',
- fields='*',
- filters={'patient': patient.name, 'company': company, 'invoiced': False}
- )
- for procedure in procedures:
- if not procedure.appointment:
- item, is_billable = frappe.get_cached_value('Clinical Procedure Template', procedure.procedure_template, ['item', 'is_billable'])
- if procedure.procedure_template and is_billable:
- clinical_procedures_to_invoice.append({
- 'reference_type': 'Clinical Procedure',
- 'reference_name': procedure.name,
- 'service': item
- })
-
- # consumables
- if procedure.invoice_separately_as_consumables and procedure.consume_stock \
- and procedure.status == 'Completed' and not procedure.consumption_invoiced:
-
- service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
- if not service_item:
- msg = _('Please Configure Clinical Procedure Consumable Item in ')
- msg += '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>'''
- frappe.throw(msg, title=_('Missing Configuration'))
-
- clinical_procedures_to_invoice.append({
- 'reference_type': 'Clinical Procedure',
- 'reference_name': procedure.name,
- 'service': service_item,
- 'rate': procedure.consumable_total_amount,
- 'description': procedure.consumption_details
- })
-
- procedure_prescriptions = frappe.db.sql(
- '''
- SELECT
- pp.name, pp.procedure
- FROM
- `tabPatient Encounter` et, `tabProcedure Prescription` pp
- WHERE
- et.patient=%s
- and pp.parent=et.name
- and pp.procedure_created=0
- and pp.invoiced=0
- and pp.appointment_booked=0
- ''', (patient.name), as_dict=1)
-
- for prescription in procedure_prescriptions:
- item, is_billable = frappe.get_cached_value('Clinical Procedure Template', prescription.procedure, ['item', 'is_billable'])
- if is_billable:
- clinical_procedures_to_invoice.append({
- 'reference_type': 'Procedure Prescription',
- 'reference_name': prescription.name,
- 'service': item
- })
-
- return clinical_procedures_to_invoice
-
-
-def get_inpatient_services_to_invoice(patient, company):
- services_to_invoice = []
- inpatient_services = frappe.db.sql(
- '''
- SELECT
- io.*
- FROM
- `tabInpatient Record` ip, `tabInpatient Occupancy` io
- WHERE
- ip.patient=%s
- and ip.company=%s
- and io.parent=ip.name
- and io.left=1
- and io.invoiced=0
- ''', (patient.name, company), as_dict=1)
-
- for inpatient_occupancy in inpatient_services:
- service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type')
- service_unit_type = frappe.get_cached_doc('Healthcare Service Unit Type', service_unit_type)
- if service_unit_type and service_unit_type.is_billable:
- hours_occupied = time_diff_in_hours(inpatient_occupancy.check_out, inpatient_occupancy.check_in)
- qty = 0.5
- if hours_occupied > 0:
- actual_qty = hours_occupied / service_unit_type.no_of_hours
- floor = math.floor(actual_qty)
- decimal_part = actual_qty - floor
- if decimal_part > 0.5:
- qty = rounded(floor + 1, 1)
- elif decimal_part < 0.5 and decimal_part > 0:
- qty = rounded(floor + 0.5, 1)
- if qty <= 0:
- qty = 0.5
- services_to_invoice.append({
- 'reference_type': 'Inpatient Occupancy',
- 'reference_name': inpatient_occupancy.name,
- 'service': service_unit_type.item, 'qty': qty
- })
-
- return services_to_invoice
-
-
-def get_therapy_plans_to_invoice(patient, company):
- therapy_plans_to_invoice = []
- therapy_plans = frappe.get_list(
- 'Therapy Plan',
- fields=['therapy_plan_template', 'name'],
- filters={
- 'patient': patient.name,
- 'invoiced': 0,
- 'company': company,
- 'therapy_plan_template': ('!=', '')
- }
- )
- for plan in therapy_plans:
- therapy_plans_to_invoice.append({
- 'reference_type': 'Therapy Plan',
- 'reference_name': plan.name,
- 'service': frappe.db.get_value('Therapy Plan Template', plan.therapy_plan_template, 'linked_item')
- })
-
- return therapy_plans_to_invoice
-
-
-def get_therapy_sessions_to_invoice(patient, company):
- therapy_sessions_to_invoice = []
- therapy_plans = frappe.db.get_all('Therapy Plan', {'therapy_plan_template': ('!=', '')})
- therapy_plans_created_from_template = []
- for entry in therapy_plans:
- therapy_plans_created_from_template.append(entry.name)
-
- therapy_sessions = frappe.get_list(
- 'Therapy Session',
- fields='*',
- filters={
- 'patient': patient.name,
- 'invoiced': 0,
- 'company': company,
- 'therapy_plan': ('not in', therapy_plans_created_from_template)
- }
- )
- for therapy in therapy_sessions:
- if not therapy.appointment:
- if therapy.therapy_type and frappe.db.get_value('Therapy Type', therapy.therapy_type, 'is_billable'):
- therapy_sessions_to_invoice.append({
- 'reference_type': 'Therapy Session',
- 'reference_name': therapy.name,
- 'service': frappe.db.get_value('Therapy Type', therapy.therapy_type, 'item')
- })
-
- return therapy_sessions_to_invoice
-
-@frappe.whitelist()
-def get_service_item_and_practitioner_charge(doc):
- if isinstance(doc, string_types):
- doc = json.loads(doc)
- doc = frappe.get_doc(doc)
-
- service_item = None
- practitioner_charge = None
- department = doc.medical_department if doc.doctype == 'Patient Encounter' else doc.department
-
- is_inpatient = doc.inpatient_record
-
- if doc.get('appointment_type'):
- service_item, practitioner_charge = get_appointment_type_service_item(doc.appointment_type, department, is_inpatient)
-
- if not service_item and not practitioner_charge:
- service_item, practitioner_charge = get_practitioner_service_item(doc.practitioner, is_inpatient)
- if not service_item:
- service_item = get_healthcare_service_item(is_inpatient)
-
- if not service_item:
- throw_config_service_item(is_inpatient)
-
- if not practitioner_charge:
- throw_config_practitioner_charge(is_inpatient, doc.practitioner)
-
- return {'service_item': service_item, 'practitioner_charge': practitioner_charge}
-
-
-def get_appointment_type_service_item(appointment_type, department, is_inpatient):
- from erpnext.healthcare.doctype.appointment_type.appointment_type import get_service_item_based_on_department
-
- item_list = get_service_item_based_on_department(appointment_type, department)
- service_item = None
- practitioner_charge = None
-
- if item_list:
- if is_inpatient:
- service_item = item_list.get('inpatient_visit_charge_item')
- practitioner_charge = item_list.get('inpatient_visit_charge')
- else:
- service_item = item_list.get('op_consulting_charge_item')
- practitioner_charge = item_list.get('op_consulting_charge')
-
- return service_item, practitioner_charge
-
-
-def throw_config_service_item(is_inpatient):
- service_item_label = _('Out Patient Consulting Charge Item')
- if is_inpatient:
- service_item_label = _('Inpatient Visit Charge Item')
-
- msg = _(('Please Configure {0} in ').format(service_item_label) \
- + '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>''')
- frappe.throw(msg, title=_('Missing Configuration'))
-
-
-def throw_config_practitioner_charge(is_inpatient, practitioner):
- charge_name = _('OP Consulting Charge')
- if is_inpatient:
- charge_name = _('Inpatient Visit Charge')
-
- msg = _(('Please Configure {0} for Healthcare Practitioner').format(charge_name) \
- + ''' <b><a href='/app/Form/Healthcare Practitioner/{0}'>{0}</a></b>'''.format(practitioner))
- frappe.throw(msg, title=_('Missing Configuration'))
-
-
-def get_practitioner_service_item(practitioner, is_inpatient):
- service_item = None
- practitioner_charge = None
-
- if is_inpatient:
- service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['inpatient_visit_charge_item', 'inpatient_visit_charge'])
- else:
- service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['op_consulting_charge_item', 'op_consulting_charge'])
-
- return service_item, practitioner_charge
-
-
-def get_healthcare_service_item(is_inpatient):
- service_item = None
-
- if is_inpatient:
- service_item = frappe.db.get_single_value('Healthcare Settings', 'inpatient_visit_charge_item')
- else:
- service_item = frappe.db.get_single_value('Healthcare Settings', 'op_consulting_charge_item')
-
- return service_item
-
-
-def get_practitioner_charge(practitioner, is_inpatient):
- if is_inpatient:
- practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'inpatient_visit_charge')
- else:
- practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, 'op_consulting_charge')
- if practitioner_charge:
- return practitioner_charge
- return False
-
-
-def manage_invoice_submit_cancel(doc, method):
- if doc.items:
- for item in doc.items:
- if item.get('reference_dt') and item.get('reference_dn'):
- if frappe.get_meta(item.reference_dt).has_field('invoiced'):
- set_invoiced(item, method, doc.name)
-
- if method=='on_submit' and frappe.db.get_single_value('Healthcare Settings', 'create_lab_test_on_si_submit'):
- create_multiple('Sales Invoice', doc.name)
-
-
-def set_invoiced(item, method, ref_invoice=None):
- invoiced = False
- if method=='on_submit':
- validate_invoiced_on_submit(item)
- invoiced = True
-
- if item.reference_dt == 'Clinical Procedure':
- service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
- if service_item == item.item_code:
- frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced)
- else:
- frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
- else:
- frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
-
- if item.reference_dt == 'Patient Appointment':
- if frappe.db.get_value('Patient Appointment', item.reference_dn, 'procedure_template'):
- dt_from_appointment = 'Clinical Procedure'
- else:
- dt_from_appointment = 'Patient Encounter'
- manage_doc_for_appointment(dt_from_appointment, item.reference_dn, invoiced)
-
- elif item.reference_dt == 'Lab Prescription':
- manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Lab Test', 'lab_test_created')
-
- elif item.reference_dt == 'Procedure Prescription':
- manage_prescriptions(invoiced, item.reference_dt, item.reference_dn, 'Clinical Procedure', 'procedure_created')
-
-
-def validate_invoiced_on_submit(item):
- if item.reference_dt == 'Clinical Procedure' and \
- frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') == item.item_code:
- is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced')
- else:
- is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced')
- if is_invoiced:
- frappe.throw(_('The item referenced by {0} - {1} is already invoiced').format(
- item.reference_dt, item.reference_dn))
-
-
-def manage_prescriptions(invoiced, ref_dt, ref_dn, dt, created_check_field):
- created = frappe.db.get_value(ref_dt, ref_dn, created_check_field)
- if created:
- # Fetch the doc created for the prescription
- doc_created = frappe.db.get_value(dt, {'prescription': ref_dn})
- frappe.db.set_value(dt, doc_created, 'invoiced', invoiced)
-
-
-def check_fee_validity(appointment):
- if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
- return
-
- validity = frappe.db.exists('Fee Validity', {
- 'practitioner': appointment.practitioner,
- 'patient': appointment.patient,
- 'valid_till': ('>=', appointment.appointment_date)
- })
- if not validity:
- return
-
- validity = frappe.get_doc('Fee Validity', validity)
- return validity
-
-
-def manage_fee_validity(appointment):
- fee_validity = check_fee_validity(appointment)
-
- if fee_validity:
- if appointment.status == 'Cancelled' and fee_validity.visited > 0:
- fee_validity.visited -= 1
- frappe.db.delete('Fee Validity Reference', {'appointment': appointment.name})
- elif fee_validity.status == 'Completed':
- return
- else:
- fee_validity.visited += 1
- fee_validity.append('ref_appointments', {
- 'appointment': appointment.name
- })
- fee_validity.save(ignore_permissions=True)
- else:
- fee_validity = create_fee_validity(appointment)
- return fee_validity
-
-
-def manage_doc_for_appointment(dt_from_appointment, appointment, invoiced):
- dn_from_appointment = frappe.db.get_value(
- dt_from_appointment,
- filters={'appointment': appointment}
- )
- if dn_from_appointment:
- frappe.db.set_value(dt_from_appointment, dn_from_appointment, 'invoiced', invoiced)
-
-
-@frappe.whitelist()
-def get_drugs_to_invoice(encounter):
- encounter = frappe.get_doc('Patient Encounter', encounter)
- if encounter:
- patient = frappe.get_doc('Patient', encounter.patient)
- if patient:
- if patient.customer:
- items_to_invoice = []
- for drug_line in encounter.drug_prescription:
- if drug_line.drug_code:
- qty = 1
- if frappe.db.get_value('Item', drug_line.drug_code, 'stock_uom') == 'Nos':
- qty = drug_line.get_quantity()
-
- description = ''
- if drug_line.dosage and drug_line.period:
- description = _('{0} for {1}').format(drug_line.dosage, drug_line.period)
-
- items_to_invoice.append({
- 'drug_code': drug_line.drug_code,
- 'quantity': qty,
- 'description': description
- })
- return items_to_invoice
- else:
- validate_customer_created(patient)
-
-
-@frappe.whitelist()
-def get_children(doctype, parent, company, is_root=False):
- parent_fieldname = "parent_" + doctype.lower().replace(" ", "_")
- fields = [
- "name as value",
- "is_group as expandable",
- "lft",
- "rgt"
- ]
- # fields = [ "name", "is_group", "lft", "rgt" ]
- filters = [["ifnull(`{0}`,'')".format(parent_fieldname), "=", "" if is_root else parent]]
-
- if is_root:
- fields += ["service_unit_type"] if doctype == "Healthcare Service Unit" else []
- filters.append(["company", "=", company])
-
- else:
- fields += ["service_unit_type", "allow_appointments", "inpatient_occupancy", "occupancy_status"] if doctype == "Healthcare Service Unit" else []
- fields += [parent_fieldname + " as parent"]
-
- hc_service_units = frappe.get_list(doctype, fields=fields, filters=filters)
-
- if doctype == "Healthcare Service Unit":
- for each in hc_service_units:
- occupancy_msg = ""
- if each["expandable"] == 1:
- occupied = False
- vacant = False
- child_list = frappe.db.sql(
- '''
- SELECT
- name, occupancy_status
- FROM
- `tabHealthcare Service Unit`
- WHERE
- inpatient_occupancy = 1
- and lft > %s and rgt < %s
- ''', (each['lft'], each['rgt']))
-
- for child in child_list:
- if not occupied:
- occupied = 0
- if child[1] == "Occupied":
- occupied += 1
- if not vacant:
- vacant = 0
- if child[1] == "Vacant":
- vacant += 1
- if vacant and occupied:
- occupancy_total = vacant + occupied
- occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
- each["occupied_out_of_vacant"] = occupancy_msg
- return hc_service_units
-
-
-@frappe.whitelist()
-def get_patient_vitals(patient, from_date=None, to_date=None):
- if not patient: return
-
- vitals = frappe.db.get_all('Vital Signs', filters={
- 'docstatus': 1,
- 'patient': patient
- }, order_by='signs_date, signs_time', fields=['*'])
-
- if len(vitals):
- return vitals
- return False
-
-
-@frappe.whitelist()
-def render_docs_as_html(docs):
- # docs key value pair {doctype: docname}
- docs_html = "<div class='col-md-12 col-sm-12 text-muted'>"
- for doc in docs:
- docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + '<br/>'
- return {'html': docs_html}
-
-
-@frappe.whitelist()
-def render_doc_as_html(doctype, docname, exclude_fields = []):
- #render document as html, three column layout will break
- doc = frappe.get_doc(doctype, docname)
- meta = frappe.get_meta(doctype)
- doc_html = "<div class='col-md-12 col-sm-12'>"
- section_html = ''
- section_label = ''
- html = ''
- sec_on = False
- col_on = 0
- has_data = False
- for df in meta.fields:
- #on section break append append previous section and html to doc html
- if df.fieldtype == "Section Break":
- if has_data and col_on and sec_on:
- doc_html += section_html + html + "</div>"
- elif has_data and not col_on and sec_on:
- doc_html += "<div class='col-md-12 col-sm-12'\
- ><div class='col-md-12 col-sm-12'>" \
- + section_html + html +"</div></div>"
- while col_on:
- doc_html += "</div>"
- col_on -= 1
- sec_on = True
- has_data= False
- col_on = 0
- section_html = ''
- html = ''
- if df.label:
- section_label = df.label
- continue
- #on column break append html to section html or doc html
- if df.fieldtype == "Column Break":
- if sec_on and has_data:
- section_html += "<div class='col-md-12 col-sm-12'\
- ><div class='col-md-6 col\
- -sm-6'><b>" + section_label + "</b>" + html + "</div><div \
- class='col-md-6 col-sm-6'>"
- elif has_data:
- doc_html += "<div class='col-md-12 col-sm-12'><div class='col-m\
- d-6 col-sm-6'>" + html + "</div><div class='col-md-6 col-sm-6'>"
- elif sec_on and not col_on:
- section_html += "<div class='col-md-6 col-sm-6'>"
- html = ''
- col_on += 1
- if df.label:
- html += '<br>' + df.label
- continue
- #on table iterate in items and create table based on in_list_view, append to section html or doc html
- if df.fieldtype == 'Table':
- items = doc.get(df.fieldname)
- if not items: continue
- child_meta = frappe.get_meta(df.options)
- if not has_data : has_data = True
- table_head = ''
- table_row = ''
- create_head = True
- for item in items:
- table_row += '<tr>'
- for cdf in child_meta.fields:
- if cdf.in_list_view:
- if create_head:
- table_head += '<th>' + cdf.label + '</th>'
- if item.get(cdf.fieldname):
- table_row += '<td>' + str(item.get(cdf.fieldname)) \
- + '</td>'
- else:
- table_row += '<td></td>'
- create_head = False
- table_row += '</tr>'
- if sec_on:
- section_html += "<table class='table table-condensed \
- bordered'>" + table_head + table_row + '</table>'
- else:
- html += "<table class='table table-condensed table-bordered'>" \
- + table_head + table_row + "</table>"
- continue
-
- #on other field types add label and value to html
- if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields:
- if doc.get(df.fieldname):
- formatted_value = format_value(doc.get(df.fieldname), meta.get_field(df.fieldname), doc)
- html += '<br>{0} : {1}'.format(df.label or df.fieldname, formatted_value)
-
- if not has_data : has_data = True
-
- if sec_on and col_on and has_data:
- doc_html += section_html + html + '</div></div>'
- elif sec_on and not col_on and has_data:
- doc_html += "<div class='col-md-12 col-sm-12'\
- ><div class='col-md-12 col-sm-12'>" \
- + section_html + html +'</div></div>'
- if doc_html:
- doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='/app/Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
-
- return {'html': doc_html}
diff --git a/erpnext/healthcare/web_form/__init__.py b/erpnext/healthcare/web_form/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/web_form/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/web_form/lab_test/__init__.py b/erpnext/healthcare/web_form/lab_test/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/web_form/lab_test/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.js b/erpnext/healthcare/web_form/lab_test/lab_test.js
deleted file mode 100644
index efcd8ab..0000000
--- a/erpnext/healthcare/web_form/lab_test/lab_test.js
+++ /dev/null
@@ -1,34 +0,0 @@
-frappe.ready(function() {
- // bind events here
- var normal_test_items = $('div[data-fieldname = "normal_test_items"]');
- var normal_test_items_add_btn = $('button[data-fieldname = "normal_test_items"]');
- var special_test_items = $('div[data-fieldname = "special_test_items"]');
- var special_test_items_add_btn = $('button[data-fieldname = "special_test_items"]');
- var sensitivity_test_items = $('div[data-fieldname = "sensitivity_test_items"]');
- var sensitivity_test_items_add_btn = $('button[data-fieldname = "sensitivity_test_items"]');
- var sensitivity_toggle = $('input[name = "sensitivity_toggle"]');
- var special_toggle = $('input[name = "special_toggle"]');
- var normal_toggle = $('input[name = "normal_toggle"]');
- if(normal_toggle.val() == 1){
- // normal_test_items[0].style.display = "none";
- // normal_test_items[0].setAttribute("hidden", true);
- // normal_test_items_add_btn[0].style.visibility = "hidden";
- special_test_items[0].style.display = "none";
- special_test_items_add_btn[0].style.display = "none";
- sensitivity_test_items[0].style.display = "none";
- sensitivity_test_items_add_btn[0].style.display = "none";
- normal_test_items_add_btn[0].style.display = "none";
- }else if(sensitivity_toggle.val() == 1){
- special_test_items[0].style.display = "none";
- special_test_items_add_btn[0].style.display = "none";
- normal_test_items[0].style.display = "none";
- normal_test_items_add_btn[0].style.display = "none";
- sensitivity_test_items_add_btn[0].style.display = "none";
- }else if(special_toggle.val() == 1){
- normal_test_items[0].style.display = "none";
- normal_test_items_add_btn[0].style.display = "none";
- sensitivity_test_items[0].style.display = "none";
- sensitivity_test_items_add_btn[0].style.display = "none";
- special_test_items_add_btn[0].style.display = "none";
- }
-});
diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.json b/erpnext/healthcare/web_form/lab_test/lab_test.json
deleted file mode 100644
index 3509917..0000000
--- a/erpnext/healthcare/web_form/lab_test/lab_test.json
+++ /dev/null
@@ -1,460 +0,0 @@
-{
- "accept_payment": 0,
- "allow_comments": 1,
- "allow_delete": 0,
- "allow_edit": 1,
- "allow_incomplete": 0,
- "allow_multiple": 1,
- "allow_print": 1,
- "amount": 0.0,
- "amount_based_on_field": 0,
- "creation": "2017-06-06 16:12:33.052258",
- "currency": "INR",
- "doc_type": "Lab Test",
- "docstatus": 0,
- "doctype": "Web Form",
- "idx": 0,
- "introduction_text": "Lab Test",
- "is_standard": 1,
- "login_required": 1,
- "max_attachment_size": 0,
- "modified": "2020-06-22 12:59:49.126398",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "lab-test",
- "owner": "Administrator",
- "payment_button_label": "Buy Now",
- "print_format": "Lab Test Print",
- "published": 1,
- "route": "lab-test",
- "route_to_success_link": 0,
- "show_attachments": 0,
- "show_in_grid": 0,
- "show_sidebar": 1,
- "sidebar_items": [],
- "success_url": "/lab-test",
- "title": "Lab Test",
- "web_form_fields": [
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "lab_test_name",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Test Name",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "department",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Department",
- "max_length": 0,
- "max_value": 0,
- "options": "Medical Department",
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "column_break_26",
- "fieldtype": "Column Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Company",
- "max_length": 0,
- "max_value": 0,
- "options": "Company",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "label": "Status",
- "max_length": 0,
- "max_value": 0,
- "options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "submitted_date",
- "fieldtype": "Datetime",
- "hidden": 0,
- "label": "Submitted Date",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sb_first",
- "fieldtype": "Section Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "patient",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Patient",
- "max_length": 0,
- "max_value": 0,
- "options": "Patient",
- "read_only": 0,
- "reqd": 1,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Patient Name",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "patient_age",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Age",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "patient_sex",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Gender",
- "max_length": 0,
- "max_value": 0,
- "options": "Gender",
- "read_only": 0,
- "reqd": 1,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "inpatient_record",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Inpatient Record",
- "max_length": 0,
- "max_value": 0,
- "options": "Inpatient Record",
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "report_preference",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Report Preference",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "email",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Email",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "mobile",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Mobile",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "c_b",
- "fieldtype": "Column Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Requesting Practitioner",
- "max_length": 0,
- "max_value": 0,
- "options": "Healthcare Practitioner",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "practitioner_name",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Requesting Practitioner",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "requesting_department",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Requesting Department",
- "max_length": 0,
- "max_value": 0,
- "options": "Medical Department",
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "employee",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Employee (Lab Technician)",
- "max_length": 0,
- "max_value": 0,
- "options": "Employee",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "employee_name",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Lab Technician Name",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "employee_designation",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Lab Technician Designation",
- "max_length": 0,
- "max_value": 0,
- "read_only": 1,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sb_normal",
- "fieldtype": "Section Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "lab_test_html",
- "fieldtype": "HTML",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "normal_test_items",
- "fieldtype": "Table",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "options": "Normal Test Result",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sb_descriptive",
- "fieldtype": "Section Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "descriptive_test_items",
- "fieldtype": "Table",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "options": "Descriptive Test Result",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "depends_on": "special_toggle",
- "fieldname": "organisms_section",
- "fieldtype": "Section Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "organisms",
- "fieldtype": "Table",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "options": "Organism Test Result",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sb_sensitivity",
- "fieldtype": "Section Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sensitivity_test_items",
- "fieldtype": "Table",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "options": "Sensitivity Test Result",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sb_comments",
- "fieldtype": "Section Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "lab_test_comment",
- "fieldtype": "Text",
- "hidden": 0,
- "label": "Comments",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sb_customresult",
- "fieldtype": "Section Break",
- "hidden": 0,
- "label": "Custom Result",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "custom_result",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "label": "Custom Result",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/web_form/lab_test/lab_test.py b/erpnext/healthcare/web_form/lab_test/lab_test.py
deleted file mode 100644
index 5a8c8a4..0000000
--- a/erpnext/healthcare/web_form/lab_test/lab_test.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def get_context(context):
- context.read_only = 1
-
-def get_list_context(context):
- context.row_template = "erpnext/templates/includes/healthcare/lab_test_row_template.html"
- context.get_list = get_lab_test_list
-
-def get_lab_test_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by='modified desc'):
- patient = get_patient()
- lab_tests = frappe.db.sql("""select * from `tabLab Test`
- where patient = %s order by result_date""", patient, as_dict = True)
- return lab_tests
-
-def get_patient():
- return frappe.get_value("Patient",{"email": frappe.session.user}, "name")
-
-def has_website_permission(doc, ptype, user, verbose=False):
- if doc.patient == get_patient():
- return True
- else:
- return False
diff --git a/erpnext/healthcare/web_form/patient_appointments/__init__.py b/erpnext/healthcare/web_form/patient_appointments/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/web_form/patient_appointments/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.js b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.js
deleted file mode 100644
index f09e540..0000000
--- a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.ready(function() {
- // bind events here
-});
diff --git a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.json b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.json
deleted file mode 100644
index e9cf7a8..0000000
--- a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.json
+++ /dev/null
@@ -1,111 +0,0 @@
-{
- "accept_payment": 0,
- "allow_comments": 0,
- "allow_delete": 0,
- "allow_edit": 1,
- "allow_incomplete": 0,
- "allow_multiple": 1,
- "allow_print": 1,
- "amount": 0.0,
- "amount_based_on_field": 0,
- "creation": "2017-06-07 15:30:44.984832",
- "currency": "INR",
- "doc_type": "Patient Appointment",
- "docstatus": 0,
- "doctype": "Web Form",
- "idx": 0,
- "introduction_text": "Patient Appointments",
- "is_standard": 1,
- "login_required": 1,
- "max_attachment_size": 0,
- "modified": "2018-07-16 13:11:08.626316",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "patient-appointments",
- "owner": "Administrator",
- "payment_button_label": "Buy Now",
- "published": 1,
- "route": "patient-appointments",
- "show_sidebar": 1,
- "sidebar_items": [],
- "success_url": "/patient-appointments",
- "title": "Patient Appointments",
- "web_form_fields": [
- {
- "fieldname": "patient",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Patient",
- "max_length": 0,
- "max_value": 0,
- "options": "Patient",
- "read_only": 0,
- "reqd": 1
- },
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Healthcare Practitioner",
- "max_length": 0,
- "max_value": 0,
- "options": "Healthcare Practitioner",
- "read_only": 0,
- "reqd": 1
- },
- {
- "fieldname": "appointment_date",
- "fieldtype": "Date",
- "hidden": 0,
- "label": "Date",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 1
- },
- {
- "fieldname": "appointment_time",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Time",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0
- },
- {
- "fieldname": "department",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Department",
- "max_length": 0,
- "max_value": 0,
- "options": "Medical Department",
- "read_only": 0,
- "reqd": 0
- },
- {
- "fieldname": "appointment_type",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Type",
- "max_length": 0,
- "max_value": 0,
- "options": "Appointment Type",
- "read_only": 0,
- "reqd": 0
- },
- {
- "default": "Scheduled",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "label": "Status",
- "max_length": 0,
- "max_value": 0,
- "options": "\nScheduled\nOpen\nClosed\nPending\nCancelled",
- "read_only": 1,
- "reqd": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.py b/erpnext/healthcare/web_form/patient_appointments/patient_appointments.py
deleted file mode 100644
index 09bcb42..0000000
--- a/erpnext/healthcare/web_form/patient_appointments/patient_appointments.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def get_context(context):
- context.read_only = 1
-
-def get_list_context(context):
- context.row_template = "erpnext/templates/includes/healthcare/appointment_row_template.html"
- context.get_list = get_appointment_list
-
-def get_appointment_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by='modified desc'):
- patient = get_patient()
- lab_tests = frappe.db.sql("""select * from `tabPatient Appointment`
- where patient = %s and (status = 'Open' or status = 'Scheduled') order by appointment_date""", patient, as_dict = True)
- return lab_tests
-
-def get_patient():
- return frappe.get_value("Patient",{"email": frappe.session.user}, "name")
-
-def has_website_permission(doc, ptype, user, verbose=False):
- if doc.patient == get_patient():
- return True
- else:
- return False
diff --git a/erpnext/healthcare/web_form/patient_registration/__init__.py b/erpnext/healthcare/web_form/patient_registration/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/web_form/patient_registration/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.js b/erpnext/healthcare/web_form/patient_registration/patient_registration.js
deleted file mode 100644
index 7da3f1f..0000000
--- a/erpnext/healthcare/web_form/patient_registration/patient_registration.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.ready(function() {
- // bind events here
-});
\ No newline at end of file
diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.json b/erpnext/healthcare/web_form/patient_registration/patient_registration.json
deleted file mode 100644
index 9ed92de..0000000
--- a/erpnext/healthcare/web_form/patient_registration/patient_registration.json
+++ /dev/null
@@ -1,397 +0,0 @@
-{
- "accept_payment": 0,
- "allow_comments": 0,
- "allow_delete": 0,
- "allow_edit": 1,
- "allow_incomplete": 0,
- "allow_multiple": 0,
- "allow_print": 0,
- "amount": 0.0,
- "amount_based_on_field": 0,
- "button_label": "Register",
- "creation": "2020-03-03 01:01:16.250607",
- "currency": "INR",
- "doc_type": "Patient",
- "docstatus": 0,
- "doctype": "Web Form",
- "idx": 0,
- "introduction_text": "",
- "is_standard": 1,
- "login_required": 0,
- "max_attachment_size": 0,
- "modified": "2020-03-26 17:25:15.361918",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "patient-registration",
- "owner": "Administrator",
- "payment_button_label": "Buy Now",
- "published": 1,
- "route": "patient-registration",
- "route_to_success_link": 0,
- "show_attachments": 0,
- "show_in_grid": 0,
- "show_sidebar": 1,
- "sidebar_items": [],
- "success_message": "Registration Successfully. Thank You!",
- "success_url": "/patient-registration",
- "title": "Patient Registration",
- "web_form_fields": [
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "basic_info",
- "fieldtype": "Section Break",
- "hidden": 0,
- "label": "Patient Demographics",
- "max_length": 0,
- "max_value": 0,
- "options": "fa fa-user",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "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 (optional)",
- "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": 1,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "sex",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Gender",
- "max_length": 0,
- "max_value": 0,
- "options": "Gender",
- "read_only": 0,
- "reqd": 1,
- "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 Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "",
- "fieldtype": "Column Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "dob",
- "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": 0,
- "fieldname": "mobile",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Mobile",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "email",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Email",
- "max_length": 0,
- "max_value": 0,
- "options": "Email",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "phone",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Phone",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "",
- "fieldtype": "Section Break",
- "hidden": 0,
- "label": "Personal Details",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "occupation",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Occupation",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "",
- "fieldtype": "Column Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "marital_status",
- "fieldtype": "Select",
- "hidden": 0,
- "label": "Marital Status",
- "max_length": 0,
- "max_value": 0,
- "options": "\nSingle\nMarried\nDivorced\nWidow",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "allergy_medical_and_surgical_history",
- "fieldtype": "Section Break",
- "hidden": 0,
- "label": "Allergies, Medical and Surgical History",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "allergies",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Allergies",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "medication",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Medication",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "column_break_20",
- "fieldtype": "Column Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "medical_history",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Medical History",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "surgical_history",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Surgical History",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "risk_factors",
- "fieldtype": "Section Break",
- "hidden": 0,
- "label": "Risk Factors",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "default": "0",
- "fieldname": "tobacco_past_use",
- "fieldtype": "Check",
- "hidden": 0,
- "label": "Check if you have a history of Tobacco Consumption",
- "max_length": 0,
- "max_value": 0,
- "options": "",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "default": "0",
- "fieldname": "tobacco_current_use",
- "fieldtype": "Check",
- "hidden": 0,
- "label": "Check if you consume Tobacco",
- "max_length": 0,
- "max_value": 0,
- "options": "",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "default": "0",
- "fieldname": "alcohol_past_use",
- "fieldtype": "Check",
- "hidden": 0,
- "label": "Check if you have a history of Alcohol Consumption",
- "max_length": 0,
- "max_value": 0,
- "options": "",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "default": "0",
- "fieldname": "alcohol_current_use",
- "fieldtype": "Check",
- "hidden": 0,
- "label": "Check if you consume Alcohol",
- "max_length": 0,
- "max_value": 0,
- "options": "",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "column_break_32",
- "fieldtype": "Column Break",
- "hidden": 0,
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "surrounding_factors",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Occupational Hazards and Environmental Factors",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "allow_read_on_all_link_options": 0,
- "fieldname": "other_risk_factors",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Other Risk Factors",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.py b/erpnext/healthcare/web_form/patient_registration/patient_registration.py
deleted file mode 100644
index 1bc4d18..0000000
--- a/erpnext/healthcare/web_form/patient_registration/patient_registration.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import unicode_literals
-
-def get_context(context):
- # do your magic here
- pass
diff --git a/erpnext/healthcare/web_form/personal_details/__init__.py b/erpnext/healthcare/web_form/personal_details/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/web_form/personal_details/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/web_form/personal_details/personal_details.js b/erpnext/healthcare/web_form/personal_details/personal_details.js
deleted file mode 100644
index f09e540..0000000
--- a/erpnext/healthcare/web_form/personal_details/personal_details.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.ready(function() {
- // bind events here
-});
diff --git a/erpnext/healthcare/web_form/personal_details/personal_details.json b/erpnext/healthcare/web_form/personal_details/personal_details.json
deleted file mode 100644
index aad987a..0000000
--- a/erpnext/healthcare/web_form/personal_details/personal_details.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "accept_payment": 0,
- "allow_comments": 0,
- "allow_delete": 0,
- "allow_edit": 1,
- "allow_incomplete": 0,
- "allow_multiple": 0,
- "allow_print": 0,
- "amount": 0.0,
- "amount_based_on_field": 0,
- "creation": "2018-07-03 19:33:23.332661",
- "currency": "INR",
- "doc_type": "Patient",
- "docstatus": 0,
- "doctype": "Web Form",
- "idx": 0,
- "introduction_text": "",
- "is_standard": 1,
- "login_required": 1,
- "max_attachment_size": 0,
- "modified": "2018-07-04 17:22:28.936442",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "personal-details",
- "owner": "Administrator",
- "payment_button_label": "Buy Now",
- "published": 1,
- "route": "personal-details",
- "show_sidebar": 1,
- "sidebar_items": [],
- "success_url": "/personal-details",
- "title": "Personal Details",
- "web_form_fields": [
- {
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Full Name",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 1
- },
- {
- "fieldname": "sex",
- "fieldtype": "Select",
- "hidden": 0,
- "label": "Gender",
- "max_length": 0,
- "max_value": 0,
- "options": "\nMale\nFemale\nOther",
- "read_only": 0,
- "reqd": 1
- },
- {
- "fieldname": "dob",
- "fieldtype": "Date",
- "hidden": 0,
- "label": "Date of birth",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 1
- },
- {
- "fieldname": "mobile",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Mobile",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0
- },
- {
- "fieldname": "email",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Email",
- "max_length": 0,
- "max_value": 0,
- "options": "Email",
- "read_only": 1,
- "reqd": 0
- }
- ]
-}
diff --git a/erpnext/healthcare/web_form/personal_details/personal_details.py b/erpnext/healthcare/web_form/personal_details/personal_details.py
deleted file mode 100644
index fe46d7b..0000000
--- a/erpnext/healthcare/web_form/personal_details/personal_details.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-from frappe import _
-
-no_cache = 1
-
-def get_context(context):
- if frappe.session.user=='Guest':
- frappe.throw(_("You need to be logged in to access this page"), frappe.PermissionError)
-
- context.show_sidebar=True
-
- if frappe.db.exists("Patient", {'email': frappe.session.user}):
- patient = frappe.get_doc("Patient", {'email': frappe.session.user})
- context.doc = patient
- frappe.form_dict.new = 0
- frappe.form_dict.name = patient.name
-
-def get_patient():
- return frappe.get_value("Patient",{"email": frappe.session.user}, "name")
-
-def has_website_permission(doc, ptype, user, verbose=False):
- if doc.name == get_patient():
- return True
- else:
- return False
diff --git a/erpnext/healthcare/web_form/prescription/__init__.py b/erpnext/healthcare/web_form/prescription/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/healthcare/web_form/prescription/__init__.py
+++ /dev/null
diff --git a/erpnext/healthcare/web_form/prescription/prescription.js b/erpnext/healthcare/web_form/prescription/prescription.js
deleted file mode 100644
index f09e540..0000000
--- a/erpnext/healthcare/web_form/prescription/prescription.js
+++ /dev/null
@@ -1,3 +0,0 @@
-frappe.ready(function() {
- // bind events here
-});
diff --git a/erpnext/healthcare/web_form/prescription/prescription.json b/erpnext/healthcare/web_form/prescription/prescription.json
deleted file mode 100644
index 8e19e32..0000000
--- a/erpnext/healthcare/web_form/prescription/prescription.json
+++ /dev/null
@@ -1,120 +0,0 @@
-{
- "accept_payment": 0,
- "allow_comments": 0,
- "allow_delete": 0,
- "allow_edit": 1,
- "allow_incomplete": 0,
- "allow_multiple": 1,
- "allow_print": 1,
- "amount": 0.0,
- "amount_based_on_field": 0,
- "creation": "2017-06-06 17:13:19.101374",
- "currency": "INR",
- "doc_type": "Patient Encounter",
- "docstatus": 0,
- "doctype": "Web Form",
- "idx": 0,
- "introduction_text": "Patient Prescriptions",
- "is_standard": 1,
- "login_required": 1,
- "max_attachment_size": 0,
- "modified": "2018-09-04 11:53:40.954517",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "prescription",
- "owner": "Administrator",
- "payment_button_label": "Buy Now",
- "print_format": "Encounter Print",
- "published": 1,
- "route": "prescription",
- "show_in_grid": 0,
- "show_sidebar": 1,
- "sidebar_items": [],
- "success_url": "/prescription",
- "title": "Prescription",
- "web_form_fields": [
- {
- "fieldname": "practitioner",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Healthcare Practitioner",
- "max_length": 0,
- "max_value": 0,
- "options": "Healthcare Practitioner",
- "read_only": 0,
- "reqd": 1,
- "show_in_filter": 0
- },
- {
- "fieldname": "visit_department",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Department",
- "max_length": 0,
- "max_value": 0,
- "options": "Medical Department",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "default": "Today",
- "fieldname": "encounter_date",
- "fieldtype": "Date",
- "hidden": 0,
- "label": "Encounter Date",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 1,
- "show_in_filter": 0
- },
- {
- "default": "",
- "fieldname": "encounter_time",
- "fieldtype": "Data",
- "hidden": 0,
- "label": "Encounter Time",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 1,
- "show_in_filter": 0
- },
- {
- "fieldname": "drug_prescription",
- "fieldtype": "Table",
- "hidden": 0,
- "label": "Drug Prescription",
- "max_length": 0,
- "max_value": 0,
- "options": "Drug Prescription",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "fieldname": "lab_test_prescription",
- "fieldtype": "Table",
- "hidden": 0,
- "label": "Investigations",
- "max_length": 0,
- "max_value": 0,
- "options": "Lab Prescription",
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- },
- {
- "fieldname": "encounter_comment",
- "fieldtype": "Small Text",
- "hidden": 0,
- "label": "Review Details",
- "max_length": 0,
- "max_value": 0,
- "read_only": 0,
- "reqd": 0,
- "show_in_filter": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/healthcare/web_form/prescription/prescription.py b/erpnext/healthcare/web_form/prescription/prescription.py
deleted file mode 100644
index efdeaa9..0000000
--- a/erpnext/healthcare/web_form/prescription/prescription.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def get_context(context):
- context.read_only = 1
-
-def get_list_context(context):
- context.row_template = "erpnext/templates/includes/healthcare/prescription_row_template.html"
- context.get_list = get_encounter_list
-
-def get_encounter_list(doctype, txt, filters, limit_start, limit_page_length = 20, order_by='modified desc'):
- patient = get_patient()
- encounters = frappe.db.sql("""select * from `tabPatient Encounter`
- where patient = %s order by creation desc""", patient, as_dict = True)
- return encounters
-
-def get_patient():
- return frappe.get_value("Patient",{"email": frappe.session.user}, "name")
-
-def has_website_permission(doc, ptype, user, verbose=False):
- if doc.patient == get_patient():
- return True
- else:
- return False
diff --git a/erpnext/healthcare/workspace/healthcare/healthcare.json b/erpnext/healthcare/workspace/healthcare/healthcare.json
deleted file mode 100644
index 55132f3..0000000
--- a/erpnext/healthcare/workspace/healthcare/healthcare.json
+++ /dev/null
@@ -1,594 +0,0 @@
-{
- "category": "",
- "charts": [
- {
- "chart_name": "Patient Appointments",
- "label": "Patient Appointments"
- }
- ],
- "charts_label": "",
- "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Healthcare\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Patient Appointments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient Appointment\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Service Unit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Practitioner\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient History\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Rehabilitation and Physiotherapy\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Records and History\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
- "creation": "2020-03-02 17:23:17.919682",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
- "docstatus": 0,
- "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
- "for_user": "",
- "hide_custom": 0,
- "icon": "healthcare",
- "idx": 0,
- "is_default": 0,
- "is_standard": 0,
- "label": "Healthcare",
- "links": [
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Masters",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient",
- "link_count": 0,
- "link_to": "Patient",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Healthcare Practitioner",
- "link_count": 0,
- "link_to": "Healthcare Practitioner",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Practitioner Schedule",
- "link_count": 0,
- "link_to": "Practitioner Schedule",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Medical Department",
- "link_count": 0,
- "link_to": "Medical Department",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Healthcare Service Unit Type",
- "link_count": 0,
- "link_to": "Healthcare Service Unit Type",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Healthcare Service Unit",
- "link_count": 0,
- "link_to": "Healthcare Service Unit",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Medical Code Standard",
- "link_count": 0,
- "link_to": "Medical Code Standard",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Medical Code",
- "link_count": 0,
- "link_to": "Medical Code",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Consultation Setup",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Appointment Type",
- "link_count": 0,
- "link_to": "Appointment Type",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Clinical Procedure Template",
- "link_count": 0,
- "link_to": "Clinical Procedure Template",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Prescription Dosage",
- "link_count": 0,
- "link_to": "Prescription Dosage",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Prescription Duration",
- "link_count": 0,
- "link_to": "Prescription Duration",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Antibiotic",
- "link_count": 0,
- "link_to": "Antibiotic",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Consultation",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient Appointment",
- "link_count": 0,
- "link_to": "Patient Appointment",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Clinical Procedure",
- "link_count": 0,
- "link_to": "Clinical Procedure",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient Encounter",
- "link_count": 0,
- "link_to": "Patient Encounter",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Vital Signs",
- "link_count": 0,
- "link_to": "Vital Signs",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Complaint",
- "link_count": 0,
- "link_to": "Complaint",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Diagnosis",
- "link_count": 0,
- "link_to": "Diagnosis",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Fee Validity",
- "link_count": 0,
- "link_to": "Fee Validity",
- "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": "Healthcare Settings",
- "link_count": 0,
- "link_to": "Healthcare Settings",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Laboratory Setup",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Lab Test Template",
- "link_count": 0,
- "link_to": "Lab Test Template",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Lab Test Sample",
- "link_count": 0,
- "link_to": "Lab Test Sample",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Lab Test UOM",
- "link_count": 0,
- "link_to": "Lab Test UOM",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Sensitivity",
- "link_count": 0,
- "link_to": "Sensitivity",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Laboratory",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Lab Test",
- "link_count": 0,
- "link_to": "Lab Test",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Sample Collection",
- "link_count": 0,
- "link_to": "Sample Collection",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Dosage Form",
- "link_count": 0,
- "link_to": "Dosage Form",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Rehabilitation and Physiotherapy",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Exercise Type",
- "link_count": 0,
- "link_to": "Exercise Type",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Therapy Type",
- "link_count": 0,
- "link_to": "Therapy Type",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Therapy Plan",
- "link_count": 0,
- "link_to": "Therapy Plan",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Therapy Session",
- "link_count": 0,
- "link_to": "Therapy Session",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient Assessment Template",
- "link_count": 0,
- "link_to": "Patient Assessment Template",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient Assessment",
- "link_count": 0,
- "link_to": "Patient Assessment",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Records and History",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient History",
- "link_count": 0,
- "link_to": "patient_history",
- "link_type": "Page",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient Progress",
- "link_count": 0,
- "link_to": "patient-progress",
- "link_type": "Page",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Patient Medical Record",
- "link_count": 0,
- "link_to": "Patient Medical Record",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Inpatient Record",
- "link_count": 0,
- "link_to": "Inpatient Record",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Reports",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Patient Appointment Analytics",
- "link_count": 0,
- "link_to": "Patient Appointment Analytics",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Lab Test Report",
- "link_count": 0,
- "link_to": "Lab Test Report",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- }
- ],
- "modified": "2021-08-05 12:15:59.434612",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Healthcare",
- "onboarding": "Healthcare",
- "owner": "Administrator",
- "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
- "public": 1,
- "restrict_to_domain": "Healthcare",
- "roles": [],
- "sequence_id": 13,
- "shortcuts": [
- {
- "color": "Orange",
- "format": "{} Open",
- "label": "Patient Appointment",
- "link_to": "Patient Appointment",
- "stats_filter": "{\n \"status\": \"Open\",\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%']\n}",
- "type": "DocType"
- },
- {
- "color": "Orange",
- "format": "{} Active",
- "label": "Patient",
- "link_to": "Patient",
- "stats_filter": "{\n \"status\": \"Active\"\n}",
- "type": "DocType"
- },
- {
- "color": "Green",
- "format": "{} Vacant",
- "label": "Healthcare Service Unit",
- "link_to": "Healthcare Service Unit",
- "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0,\n \"company\": [\"like\", \"%\" + frappe.defaults.get_global_default(\"company\") + \"%\"]\n}",
- "type": "DocType"
- },
- {
- "label": "Healthcare Practitioner",
- "link_to": "Healthcare Practitioner",
- "type": "DocType"
- },
- {
- "label": "Patient History",
- "link_to": "patient_history",
- "type": "Page"
- },
- {
- "label": "Dashboard",
- "link_to": "Healthcare",
- "type": "Dashboard"
- }
- ],
- "title": "Healthcare"
-}
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 8f7c7db..05f07f5 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
from frappe import _
app_name = "erpnext"
@@ -61,6 +62,7 @@
# website
update_website_context = ["erpnext.shopping_cart.utils.update_website_context", "erpnext.education.doctype.education_settings.education_settings.update_website_context"]
my_account_context = "erpnext.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"]
@@ -68,7 +70,6 @@
'Agriculture': 'erpnext.domains.agriculture',
'Distribution': 'erpnext.domains.distribution',
'Education': 'erpnext.domains.education',
- 'Healthcare': 'erpnext.domains.healthcare',
'Hospitality': 'erpnext.domains.hospitality',
'Manufacturing': 'erpnext.domains.manufacturing',
'Non Profit': 'erpnext.domains.non_profit',
@@ -80,7 +81,7 @@
"Job Opening", "Student Admission"]
website_context = {
- "favicon": "/assets/erpnext/images/erpnext-favicon.svg",
+ "favicon": "/assets/erpnext/images/erpnext-favicon.svg",
"splash_image": "/assets/erpnext/images/erpnext-logo.svg"
}
@@ -163,7 +164,6 @@
]
standard_portal_menu_items = [
- {"title": _("Personal Details"), "route": "/personal-details", "reference_doctype": "Patient", "role": "Patient"},
{"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"},
@@ -176,9 +176,6 @@
{"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": _("Lab Test"), "route": "/lab-test", "reference_doctype": "Lab Test", "role":"Patient"},
- {"title": _("Prescription"), "route": "/prescription", "reference_doctype": "Patient Encounter", "role":"Patient"},
- {"title": _("Patient Appointment"), "route": "/patient-appointments", "reference_doctype": "Patient Appointment", "role":"Patient"},
{"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"},
{"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"},
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"},
@@ -213,10 +210,6 @@
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
"Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission",
- "Lab Test": "erpnext.healthcare.web_form.lab_test.lab_test.has_website_permission",
- "Patient Encounter": "erpnext.healthcare.web_form.prescription.prescription.has_website_permission",
- "Patient Appointment": "erpnext.healthcare.web_form.patient_appointments.patient_appointments.has_website_permission",
- "Patient": "erpnext.healthcare.web_form.personal_details.personal_details.has_website_permission"
}
dump_report_map = "erpnext.startup.report_data_map.data_map"
@@ -225,15 +218,11 @@
standard_queries = {
"Customer": "erpnext.selling.doctype.customer.customer.get_customer_list",
- "Healthcare Practitioner": "erpnext.healthcare.doctype.healthcare_practitioner.healthcare_practitioner.get_practitioner_list"
}
doc_events = {
"*": {
"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
- "on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record",
- "on_update_after_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record",
- "on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record"
},
"Stock Entry": {
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
@@ -251,7 +240,7 @@
"erpnext.support.doctype.issue.issue.set_first_response_time"
]
},
- ("Sales Taxes and Charges Template", 'Price List'): {
+ "Sales Taxes and Charges Template": {
"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"
},
"Website Settings": {
@@ -261,6 +250,7 @@
"validate": "erpnext.regional.india.utils.validate_tax_category"
},
"Sales Invoice": {
+ "after_insert": "erpnext.regional.saudi_arabia.utils.create_qr_code",
"on_submit": [
"erpnext.regional.create_transaction_log",
"erpnext.regional.italy.utils.sales_invoice_on_submit",
@@ -270,7 +260,10 @@
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
],
- "on_trash": "erpnext.regional.check_deletion_permission",
+ "on_trash": [
+ "erpnext.regional.check_deletion_permission",
+ "erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
+ ],
"validate": [
"erpnext.regional.india.utils.validate_document_name",
"erpnext.regional.india.utils.update_taxable_values"
@@ -286,11 +279,16 @@
]
},
"Payment Entry": {
+ "validate": "erpnext.regional.india.utils.update_place_of_supply",
"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.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category']
+ 'validate': [
+ 'erpnext.regional.india.utils.validate_gstin_for_india',
+ 'erpnext.regional.italy.utils.set_state_code',
+ 'erpnext.regional.india.utils.update_gst_category',
+ ],
},
'Supplier': {
'validate': 'erpnext.regional.india.utils.validate_pan_for_india'
@@ -301,13 +299,19 @@
"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"]
+ },
+ "Company": {
+ "on_trash": "erpnext.regional.india.utils.delete_gst_settings_for_company"
+ },
+ "Integration Request": {
+ "validate": "erpnext.accounts.doctype.payment_request.payment_request.validate_payment"
}
}
@@ -316,7 +320,6 @@
# if payment entry not in auto cancel exempted doctypes it will cancel payment entry.
auto_cancel_exempted_doctypes= [
"Payment Entry",
- "Inpatient Medication Entry"
]
after_migrate = ["erpnext.setup.install.update_select_perm_after_install"]
@@ -329,7 +332,7 @@
},
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder",
- "erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder",
+ "erpnext.hr.doctype.interview.interview.send_interview_reminder",
"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts"
],
"hourly": [
@@ -352,7 +355,8 @@
"erpnext.crm.doctype.opportunity.opportunity.auto_close_opportunity",
"erpnext.controllers.accounts_controller.update_invoice_status",
"erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year",
- "erpnext.hr.doctype.employee.employee.send_birthday_reminders",
+ "erpnext.hr.doctype.employee.employee_reminders.send_work_anniversary_reminders",
+ "erpnext.hr.doctype.employee.employee_reminders.send_birthday_reminders",
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
"erpnext.assets.doctype.asset.depreciation.post_depreciation_entries",
"erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.send_summary",
@@ -369,10 +373,10 @@
"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
- "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_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.non_profit.doctype.membership.membership.set_expired_status"
+ "erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder"
],
"daily_long": [
"erpnext.setup.doctype.email_digest.email_digest.send",
@@ -384,6 +388,12 @@
"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"
],
+ "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"
@@ -421,7 +431,7 @@
"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"
+ "Subscription Plan", "POS Invoice", "POS Invoice Item"
]
regional_overrides = {
@@ -436,7 +446,6 @@
'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts',
'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
- 'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount',
'erpnext.stock.doctype.item.item.set_item_tax_from_hsn_code': 'erpnext.regional.india.utils.set_item_tax_from_hsn_code'
},
@@ -517,32 +526,6 @@
{"doctype": "Maintenance Visit", "index": 46},
{"doctype": "Warranty Claim", "index": 47},
],
- "Healthcare": [
- {'doctype': 'Patient', 'index': 1},
- {'doctype': 'Medical Department', 'index': 2},
- {'doctype': 'Vital Signs', 'index': 3},
- {'doctype': 'Healthcare Practitioner', 'index': 4},
- {'doctype': 'Patient Appointment', 'index': 5},
- {'doctype': 'Healthcare Service Unit', 'index': 6},
- {'doctype': 'Patient Encounter', 'index': 7},
- {'doctype': 'Antibiotic', 'index': 8},
- {'doctype': 'Diagnosis', 'index': 9},
- {'doctype': 'Lab Test', 'index': 10},
- {'doctype': 'Clinical Procedure', 'index': 11},
- {'doctype': 'Inpatient Record', 'index': 12},
- {'doctype': 'Sample Collection', 'index': 13},
- {'doctype': 'Patient Medical Record', 'index': 14},
- {'doctype': 'Appointment Type', 'index': 15},
- {'doctype': 'Fee Validity', 'index': 16},
- {'doctype': 'Practitioner Schedule', 'index': 17},
- {'doctype': 'Dosage Form', 'index': 18},
- {'doctype': 'Lab Test Sample', 'index': 19},
- {'doctype': 'Prescription Duration', 'index': 20},
- {'doctype': 'Prescription Dosage', 'index': 21},
- {'doctype': 'Sensitivity', 'index': 22},
- {'doctype': 'Complaint', 'index': 23},
- {'doctype': 'Medical Code', 'index': 24},
- ],
"Education": [
{'doctype': 'Article', 'index': 1},
{'doctype': 'Video', 'index': 2},
diff --git a/erpnext/hotels/doctype/hotel_room/hotel_room.py b/erpnext/hotels/doctype/hotel_room/hotel_room.py
index 8471aee..93a62c9 100644
--- a/erpnext/hotels/doctype/hotel_room/hotel_room.py
+++ b/erpnext/hotels/doctype/hotel_room/hotel_room.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class HotelRoom(Document):
def validate(self):
if not self.capacity:
self.capacity, self.extra_bed_capacity = frappe.db.get_value('Hotel Room Type',
- self.hotel_room_type, ['capacity', 'extra_bed_capacity'])
\ No newline at end of file
+ self.hotel_room_type, ['capacity', 'extra_bed_capacity'])
diff --git a/erpnext/hotels/doctype/hotel_room/test_hotel_room.js b/erpnext/hotels/doctype/hotel_room/test_hotel_room.js
deleted file mode 100644
index 8b2b833..0000000
--- a/erpnext/hotels/doctype/hotel_room/test_hotel_room.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Room
- () => frappe.tests.make('Hotel Room', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room/test_hotel_room.py b/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
index e307b5a..4fedbd4 100644
--- a/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
+++ b/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
test_dependencies = ["Hotel Room Package"]
test_records = [
dict(doctype="Hotel Room", name="1001",
diff --git a/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
index 69da007..982b3ef 100644
--- a/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
+++ b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelRoomAmenity(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
index 8a62eea..1864081 100644
--- a/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
+++ b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class HotelRoomPackage(Document):
def validate(self):
if not self.item:
diff --git a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js
deleted file mode 100644
index f1ebad4..0000000
--- a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Package", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Room Package
- () => frappe.tests.make('Hotel Room Package', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py
index ebf7f2b..fe5d79d 100644
--- a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py
+++ b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
test_records = [
dict(doctype='Item', item_code='Breakfast',
item_group='Products', is_stock_item=0),
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
index 8eee0f2..5797fef 100644
--- a/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
+++ b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelRoomPricing(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js
deleted file mode 100644
index ba0d1fd..0000000
--- a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Pricing", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Room Pricing
- () => frappe.tests.make('Hotel Room Pricing', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py
index b73fd44..72030c6 100644
--- a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py
+++ b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
test_dependencies = ["Hotel Room Package"]
test_records = [
dict(doctype="Hotel Room Pricing", enabled=1,
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py b/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py
index 6bf01bf..4a344df 100644
--- a/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py
+++ b/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelRoomPricingItem(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py
index 9ae9fcf..f594ac7 100644
--- a/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelRoomPricingPackage(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js
deleted file mode 100644
index 73a561c..0000000
--- a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Pricing Package", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Room Pricing Package
- () => frappe.tests.make('Hotel Room Pricing Package', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py
index fec1c86..ea258cc 100644
--- a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestHotelRoomPricingPackage(unittest.TestCase):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
index a8ebe86..4944862 100644
--- a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
@@ -3,10 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.model.document import Document
+
+import json
+
+import frappe
from frappe import _
-from frappe.utils import date_diff, add_days, flt
+from frappe.model.document import Document
+from frappe.utils import add_days, date_diff, flt
+
class HotelRoomUnavailableError(frappe.ValidationError): pass
class HotelRoomPricingNotSetError(frappe.ValidationError): pass
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js
index 7f7322c..7bde292 100644
--- a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js
@@ -6,4 +6,4 @@
"title": "guest_name",
"status": "status"
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js
deleted file mode 100644
index 2897139..0000000
--- a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Reservation", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Room Reservation
- () => frappe.tests.make('Hotel Room Reservation', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py
index d497996..e03005c 100644
--- a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py
+++ b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py
@@ -3,9 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import HotelRoomPricingNotSetError, HotelRoomUnavailableError
+
+import frappe
+
+from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import (
+ HotelRoomPricingNotSetError,
+ HotelRoomUnavailableError,
+)
+
test_dependencies = ["Hotel Room Package", "Hotel Room Pricing", "Hotel Room"]
class TestHotelRoomReservation(unittest.TestCase):
diff --git a/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py b/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py
index 3406fae..0cf8547 100644
--- a/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py
+++ b/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelRoomReservationItem(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
index 1fc1303..610cf18 100644
--- a/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
+++ b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelRoomType(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js
deleted file mode 100644
index e2dd578..0000000
--- a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Room Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Room Type
- () => frappe.tests.make('Hotel Room Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py
index 3b243e9..6dba7b7 100644
--- a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py
+++ b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestHotelRoomType(unittest.TestCase):
pass
diff --git a/erpnext/hotels/doctype/hotel_settings/hotel_settings.py b/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
index d78bca1..f8f8fe9 100644
--- a/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
+++ b/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HotelSettings(Document):
pass
diff --git a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js
deleted file mode 100644
index bc0b7f8..0000000
--- a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Hotel Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Hotel Settings
- () => frappe.tests.make('Hotel Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
index a081acc..5cf58b9 100644
--- a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
+++ b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestHotelSettings(unittest.TestCase):
pass
diff --git a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
index f77d43b..f02baeb 100644
--- a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
+++ b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
@@ -2,12 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import add_days, date_diff
from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import get_rooms_booked
+
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
@@ -30,4 +32,4 @@
out.append([room_type.name, total_booked])
- return out
\ No newline at end of file
+ return out
diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
index 85b82c5..b9a8ec6 100644
--- a/erpnext/hr/doctype/appointment_letter/appointment_letter.py
+++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class AppointmentLetter(Document):
pass
diff --git a/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py b/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py
index b9ce981..88637b9 100644
--- a/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py
+++ b/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestAppointmentLetter(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py
index a1a49e5..f4db456 100644
--- a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py
+++ b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AppointmentLettercontent(Document):
pass
diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py
index c23881f..acb5c1f 100644
--- a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py
+++ b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class AppointmentLetterTemplate(Document):
pass
diff --git a/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py b/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py
index 3d061ac..46dd3e1 100644
--- a/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py
+++ b/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestAppointmentLetterTemplate(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/appraisal/appraisal.js b/erpnext/hr/doctype/appraisal/appraisal.js
index 1a30cea..50612b9 100644
--- a/erpnext/hr/doctype/appraisal/appraisal.js
+++ b/erpnext/hr/doctype/appraisal/appraisal.js
@@ -15,7 +15,7 @@
frm.set_value('status', 'Draft');
}
},
-
+
kra_template: function(frm) {
frm.doc.goals = [];
erpnext.utils.map_current_doc({
diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py
index c2ed457..96a4ffa 100644
--- a/erpnext/hr/doctype/appraisal/appraisal.py
+++ b/erpnext/hr/doctype/appraisal/appraisal.py
@@ -2,15 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
from frappe.utils import flt, getdate
-from frappe import _
-from frappe.model.mapper import get_mapped_doc
-from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name, validate_active_employee
+
class Appraisal(Document):
def validate(self):
if not self.status:
diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.js b/erpnext/hr/doctype/appraisal/test_appraisal.js
index 9ca17e2..fb1354c 100644
--- a/erpnext/hr/doctype/appraisal/test_appraisal.js
+++ b/erpnext/hr/doctype/appraisal/test_appraisal.js
@@ -55,4 +55,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.py b/erpnext/hr/doctype/appraisal/test_appraisal.py
index f70dc48..cf2bd7c 100644
--- a/erpnext/hr/doctype/appraisal/test_appraisal.py
+++ b/erpnext/hr/doctype/appraisal/test_appraisal.py
@@ -2,7 +2,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Appraisal')
diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py
index a6868ee..d9789a0 100644
--- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py
+++ b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class AppraisalGoal(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py
index d0dfad4..d10a0de 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint, flt
-from frappe import _
+import frappe
+from frappe import _
from frappe.model.document import Document
+from frappe.utils import cint, flt
+
class AppraisalTemplate(Document):
def validate(self):
diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
index 309427e..b8d0494 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Appraisal']
},
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js
index 0403cad..3eb64e0 100644
--- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js
+++ b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js
@@ -27,4 +27,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
index e3029d9..a814ec8 100644
--- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
+++ b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.py
@@ -2,7 +2,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Appraisal Template')
diff --git a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py
index ca58e0c..1b15fbd 100644
--- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py
+++ b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class AppraisalTemplateGoal(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index c1a7c8f..002f9bb 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -2,14 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import getdate, nowdate
+import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import cstr, get_datetime, formatdate
+from frappe.utils import cstr, formatdate, get_datetime, getdate, nowdate
+
from erpnext.hr.utils import validate_active_employee
+
class Attendance(Document):
def validate(self):
from erpnext.controllers.status_updater import validate_status
@@ -134,7 +135,6 @@
@frappe.whitelist()
def mark_bulk_attendance(data):
import json
- from pprint import pprint
if isinstance(data, str):
data = json.loads(data)
data = frappe._dict(data)
diff --git a/erpnext/hr/doctype/attendance/attendance_calendar.js b/erpnext/hr/doctype/attendance/attendance_calendar.js
index 4566489..d9f6d2e 100644
--- a/erpnext/hr/doctype/attendance/attendance_calendar.js
+++ b/erpnext/hr/doctype/attendance/attendance_calendar.js
@@ -9,4 +9,4 @@
}
},
get_events_method: "erpnext.hr.doctype.attendance.attendance.get_events"
-};
\ No newline at end of file
+};
diff --git a/erpnext/hr/doctype/attendance/attendance_dashboard.py b/erpnext/hr/doctype/attendance/attendance_dashboard.py
index 5dd9403..bbe67df 100644
--- a/erpnext/hr/doctype/attendance/attendance_dashboard.py
+++ b/erpnext/hr/doctype/attendance/attendance_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
diff --git a/erpnext/hr/doctype/attendance/attendance_list.js b/erpnext/hr/doctype/attendance/attendance_list.js
index 9a3bac0..6b3c29a 100644
--- a/erpnext/hr/doctype/attendance/attendance_list.js
+++ b/erpnext/hr/doctype/attendance/attendance_list.js
@@ -9,83 +9,86 @@
return [__(doc.status), "orange", "status,=," + doc.status];
}
},
+
onload: function(list_view) {
let me = this;
- const months = moment.months()
- list_view.page.add_inner_button( __("Mark Attendance"), function() {
+ const months = moment.months();
+ list_view.page.add_inner_button(__("Mark Attendance"), function() {
let dialog = new frappe.ui.Dialog({
title: __("Mark Attendance"),
- fields: [
- {
- fieldname: 'employee',
- label: __('For Employee'),
- fieldtype: 'Link',
- options: 'Employee',
- get_query: () => {
- return {query: "erpnext.controllers.queries.employee_query"}
- },
- reqd: 1,
- onchange: function() {
- dialog.set_df_property("unmarked_days", "hidden", 1);
- dialog.set_df_property("status", "hidden", 1);
- dialog.set_df_property("month", "value", '');
+ fields: [{
+ fieldname: 'employee',
+ label: __('For Employee'),
+ fieldtype: 'Link',
+ options: 'Employee',
+ get_query: () => {
+ return {query: "erpnext.controllers.queries.employee_query"};
+ },
+ reqd: 1,
+ onchange: function() {
+ dialog.set_df_property("unmarked_days", "hidden", 1);
+ dialog.set_df_property("status", "hidden", 1);
+ dialog.set_df_property("month", "value", '');
+ dialog.set_df_property("unmarked_days", "options", []);
+ dialog.no_unmarked_days_left = false;
+ }
+ },
+ {
+ label: __("For Month"),
+ fieldtype: "Select",
+ fieldname: "month",
+ options: months,
+ reqd: 1,
+ onchange: function() {
+ if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
+ dialog.set_df_property("status", "hidden", 0);
dialog.set_df_property("unmarked_days", "options", []);
dialog.no_unmarked_days_left = false;
+ me.get_multi_select_options(dialog.fields_dict.employee.value, dialog.fields_dict.month.value).then(options => {
+ if (options.length > 0) {
+ dialog.set_df_property("unmarked_days", "hidden", 0);
+ dialog.set_df_property("unmarked_days", "options", options);
+ } else {
+ dialog.no_unmarked_days_left = true;
+ }
+ });
}
- },
- {
- label: __("For Month"),
- fieldtype: "Select",
- fieldname: "month",
- options: months,
- reqd: 1,
- onchange: function() {
- if(dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
- dialog.set_df_property("status", "hidden", 0);
- dialog.set_df_property("unmarked_days", "options", []);
- dialog.no_unmarked_days_left = false;
- me.get_multi_select_options(dialog.fields_dict.employee.value, dialog.fields_dict.month.value).then(options =>{
- if (options.length > 0) {
- dialog.set_df_property("unmarked_days", "hidden", 0);
- dialog.set_df_property("unmarked_days", "options", options);
- } else {
- dialog.no_unmarked_days_left = true;
- }
- });
- }
- }
- },
- {
- label: __("Status"),
- fieldtype: "Select",
- fieldname: "status",
- options: ["Present", "Absent", "Half Day", "Work From Home"],
- hidden:1,
- reqd: 1,
+ }
+ },
+ {
+ label: __("Status"),
+ fieldtype: "Select",
+ fieldname: "status",
+ options: ["Present", "Absent", "Half Day", "Work From Home"],
+ hidden: 1,
+ reqd: 1,
- },
- {
- label: __("Unmarked Attendance for days"),
- fieldname: "unmarked_days",
- fieldtype: "MultiCheck",
- options: [],
- columns: 2,
- hidden: 1
- },
- ],
- primary_action(data) {
+ },
+ {
+ label: __("Unmarked Attendance for days"),
+ fieldname: "unmarked_days",
+ fieldtype: "MultiCheck",
+ options: [],
+ columns: 2,
+ hidden: 1
+ }],
+ primary_action(data) {
if (cur_dialog.no_unmarked_days_left) {
- frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}",[dialog.fields_dict.month.value, dialog.fields_dict.employee.value]));
+ frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}",
+ [dialog.fields_dict.month.value, dialog.fields_dict.employee.value]));
} else {
- frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status,data.month]), () => {
+ frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status, data.month]), () => {
frappe.call({
method: "erpnext.hr.doctype.attendance.attendance.mark_bulk_attendance",
args: {
data: data
},
- callback: function(r) {
+ callback: function (r) {
if (r.message === 1) {
- frappe.show_alert({message: __("Attendance Marked"), indicator: 'blue'});
+ frappe.show_alert({
+ message: __("Attendance Marked"),
+ indicator: 'blue'
+ });
cur_dialog.hide();
}
}
@@ -101,21 +104,26 @@
dialog.show();
});
},
- get_multi_select_options: function(employee, month){
+
+ get_multi_select_options: function(employee, month) {
return new Promise(resolve => {
frappe.call({
method: 'erpnext.hr.doctype.attendance.attendance.get_unmarked_days',
async: false,
- args:{
+ args: {
employee: employee,
month: month,
}
}).then(r => {
var options = [];
- for(var d in r.message){
+ for (var d in r.message) {
var momentObj = moment(r.message[d], 'YYYY-MM-DD');
var date = momentObj.format('DD-MM-YYYY');
- options.push({ "label":date, "value": r.message[d] , "checked": 1});
+ options.push({
+ "label": date,
+ "value": r.message[d],
+ "checked": 1
+ });
}
resolve(options);
});
diff --git a/erpnext/hr/doctype/attendance/test_attendance.js b/erpnext/hr/doctype/attendance/test_attendance.js
index 8f30e8c..b3e7fef 100644
--- a/erpnext/hr/doctype/attendance/test_attendance.js
+++ b/erpnext/hr/doctype/attendance/test_attendance.js
@@ -36,4 +36,4 @@
"attendance for Present day is marked"),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/attendance/test_attendance.py b/erpnext/hr/doctype/attendance/test_attendance.py
index 838b704..ab44377 100644
--- a/erpnext/hr/doctype/attendance/test_attendance.py
+++ b/erpnext/hr/doctype/attendance/test_attendance.py
@@ -2,8 +2,9 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import nowdate
test_records = frappe.get_test_records('Attendance')
diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py
index 7f88fed..1e74296 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import date_diff, add_days, getdate
+from frappe.utils import add_days, date_diff, getdate
+
from erpnext.hr.doctype.employee.employee import is_holiday
-from erpnext.hr.utils import validate_dates, validate_active_employee
+from erpnext.hr.utils import validate_active_employee, validate_dates
+
class AttendanceRequest(Document):
def validate(self):
diff --git a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
index cfdd6d3..8feb6f2 100644
--- a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
+++ b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'attendance_request',
@@ -8,4 +9,4 @@
'items': ['Attendance']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.js b/erpnext/hr/doctype/attendance_request/test_attendance_request.js
deleted file mode 100644
index d40ec61..0000000
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Attendance Request", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Attendance Request
- () => frappe.tests.make('Attendance Request', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
index 9e668aa..a9db74c 100644
--- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py
+++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py
@@ -3,11 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate
from datetime import date
+import frappe
+from frappe.utils import nowdate
+
test_dependencies = ["Employee"]
class TestAttendanceRequest(unittest.TestCase):
diff --git a/erpnext/hr/doctype/branch/branch.py b/erpnext/hr/doctype/branch/branch.py
index fab2ffc..c770dc3 100644
--- a/erpnext/hr/doctype/branch/branch.py
+++ b/erpnext/hr/doctype/branch/branch.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class Branch(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/branch/test_branch.js b/erpnext/hr/doctype/branch/test_branch.js
deleted file mode 100644
index c315385..0000000
--- a/erpnext/hr/doctype/branch/test_branch.js
+++ /dev/null
@@ -1,23 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Branch [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // test branch creation
- () => frappe.set_route("List", "Branch", "List"),
- () => frappe.new_doc("Branch"),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("branch", "Test Branch"),
-
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Branch", cur_frm.doc.branch,
- 'name of branch correctly saved'),
- () => done()
- ]);
-});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/branch/test_branch.py b/erpnext/hr/doctype/branch/test_branch.py
index 5ba02b3..7bf9b39 100644
--- a/erpnext/hr/doctype/branch/test_branch.py
+++ b/erpnext/hr/doctype/branch/test_branch.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Branch')
\ No newline at end of file
+
+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 0d7fded..f24483b 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
@@ -3,12 +3,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import date_diff, add_days, getdate, cint, format_date
from frappe.model.document import Document
-from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, validate_active_employee, \
- get_holidays_for_employee, create_additional_leave_ledger_entry
+from frappe.utils import add_days, cint, date_diff, format_date, getdate
+
+from erpnext.hr.utils import (
+ create_additional_leave_ledger_entry,
+ get_holiday_dates_for_employee,
+ get_leave_period,
+ validate_active_employee,
+ validate_dates,
+ validate_overlap,
+)
+
class CompensatoryLeaveRequest(Document):
@@ -39,7 +48,7 @@
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
def validate_holidays(self):
- holidays = get_holidays_for_employee(self.employee, self.work_from_date, self.work_end_date)
+ 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)))
diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.js b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.js
deleted file mode 100644
index bebcaac..0000000
--- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Compensatory Leave Request", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Compensatory Leave Request
- () => frappe.tests.make('Compensatory Leave Request', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 3b99c57..95bdd51 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
@@ -3,12 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import today, add_months, add_days
+
+import frappe
+from frappe.utils import add_days, add_months, today
+
from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee
-from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
+from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
test_dependencies = ["Employee"]
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 1cc2381..92cf6aa 100644
--- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py
@@ -3,11 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
-from frappe import _
from email_reply_parser import EmailReplyParser
-from erpnext.hr.doctype.employee.employee import is_holiday
+from frappe import _
+from frappe.model.document import Document
from frappe.utils import global_date_format
from six import string_types
@@ -82,7 +82,7 @@
crop=True
)
d.image = thumbnail_image
- except:
+ except Exception:
d.image = original_image
if d.sender in did_not_reply:
diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js
deleted file mode 100644
index d2ceb8b..0000000
--- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Daily Work Summary", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Daily Work Summary
- () => frappe.tests.make('Daily Work Summary', [
- // values to be set
- { key: 'value' }
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
\ No newline at end of file
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 3868479..bed12e3 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
@@ -4,8 +4,9 @@
from __future__ import unicode_literals
import os
-import frappe
import unittest
+
+import frappe
import frappe.utils
# test_records = frappe.get_test_records('Daily Work Summary')
@@ -64,8 +65,7 @@
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
+ from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import trigger_emails
trigger_emails()
# check if emails are created
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 ece331a..152b1a9 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
@@ -3,13 +3,16 @@
# # For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
import frappe.utils
from frappe import _
+from frappe.model.document import Document
+
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
+
class DailyWorkSummaryGroup(Document):
def validate(self):
if self.users:
diff --git a/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py
index eefcc0c..d69a7fb 100644
--- a/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py
+++ b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class DailyWorkSummaryGroupUser(Document):
pass
diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py
index 539a360..b4771b3 100644
--- a/erpnext/hr/doctype/department/department.py
+++ b/erpnext/hr/doctype/department/department.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils.nestedset import NestedSet, get_root_of
+
from erpnext.utilities.transaction_base import delete_events
-from frappe.model.document import Document
+
class Department(NestedSet):
nsm_parent_field = 'parent_department'
diff --git a/erpnext/hr/doctype/department/department_tree.js b/erpnext/hr/doctype/department/department_tree.js
index 52d864b..5c7726d 100644
--- a/erpnext/hr/doctype/department/department_tree.js
+++ b/erpnext/hr/doctype/department/department_tree.js
@@ -25,4 +25,4 @@
onload: function(treeview) {
treeview.make_tree();
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/hr/doctype/department/test_department.js b/erpnext/hr/doctype/department/test_department.js
deleted file mode 100644
index 3a571f7..0000000
--- a/erpnext/hr/doctype/department/test_department.js
+++ /dev/null
@@ -1,23 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Department [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // test department creation
- () => frappe.set_route("List", "Department", "List"),
- () => frappe.new_doc("Department"),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("department_name", "Test Department"),
- () => cur_frm.set_value("leave_block_list", "Test Leave block list"),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Department", cur_frm.doc.department_name,
- 'name of department correctly saved'),
- () => done()
- ]);
-});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/department/test_department.py b/erpnext/hr/doctype/department/test_department.py
index 2eeca26..2fb3b95 100644
--- a/erpnext/hr/doctype/department/test_department.py
+++ b/erpnext/hr/doctype/department/test_department.py
@@ -1,9 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
+import frappe
+
test_ignore = ["Leave Block List"]
class TestDepartment(unittest.TestCase):
def test_remove_department_data(self):
@@ -21,4 +23,4 @@
return doc
-test_records = frappe.get_test_records('Department')
\ No newline at end of file
+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 d337959..113ea18 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class DepartmentApprover(Document):
pass
diff --git a/erpnext/hr/doctype/designation/designation.py b/erpnext/hr/doctype/designation/designation.py
index efd864a..0291a99 100644
--- a/erpnext/hr/doctype/designation/designation.py
+++ b/erpnext/hr/doctype/designation/designation.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class Designation(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/designation/test_designation.js b/erpnext/hr/doctype/designation/test_designation.js
deleted file mode 100644
index 45c3417..0000000
--- a/erpnext/hr/doctype/designation/test_designation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-QUnit.module('hr');
-
-QUnit.test("Test: Designation [HR]", function (assert) {
- assert.expect(1);
- let done = assert.async();
-
- frappe.run_serially([
- // test designation creation
- () => frappe.set_route("List", "Designation", "List"),
- () => frappe.new_doc("Designation"),
- () => frappe.timeout(1),
- () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
- () => frappe.timeout(1),
- () => cur_frm.set_value("designation_name", "Test Designation"),
- () => cur_frm.set_value("description", "This designation is just for testing."),
- // save form
- () => cur_frm.save(),
- () => frappe.timeout(1),
- () => assert.equal("Test Designation", cur_frm.doc.designation_name,
- 'name of designation correctly saved'),
- () => done()
- ]);
-});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py
index 3b30094..33aa243 100644
--- a/erpnext/hr/doctype/designation/test_designation.py
+++ b/erpnext/hr/doctype/designation/test_designation.py
@@ -2,8 +2,8 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
+
# test_records = frappe.get_test_records('Designation')
def create_designation(**args):
@@ -17,4 +17,4 @@
"description": args.description or "_Test description"
})
designation.save()
- return designation
\ No newline at end of file
+ return designation
diff --git a/erpnext/hr/doctype/designation_skill/designation_skill.py b/erpnext/hr/doctype/designation_skill/designation_skill.py
index c37d21f..2074dc9 100644
--- a/erpnext/hr/doctype/designation_skill/designation_skill.py
+++ b/erpnext/hr/doctype/designation_skill/designation_skill.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class DesignationSkill(Document):
pass
diff --git a/erpnext/hr/doctype/driver/driver.py b/erpnext/hr/doctype/driver/driver.py
index 2cd22cd..5c428b5 100644
--- a/erpnext/hr/doctype/driver/driver.py
+++ b/erpnext/hr/doctype/driver/driver.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class Driver(Document):
pass
diff --git a/erpnext/hr/doctype/driver/test_driver.js b/erpnext/hr/doctype/driver/test_driver.js
deleted file mode 100644
index ff9f61e..0000000
--- a/erpnext/hr/doctype/driver/test_driver.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Driver", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Driver
- () => frappe.tests.make('Driver', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/driver/test_driver.py b/erpnext/hr/doctype/driver/test_driver.py
index 4bc4a8f..fa36237 100644
--- a/erpnext/hr/doctype/driver/test_driver.py
+++ b/erpnext/hr/doctype/driver/test_driver.py
@@ -5,5 +5,6 @@
import unittest
+
class TestDriver(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/driving_license_category/driving_license_category.py b/erpnext/hr/doctype/driving_license_category/driving_license_category.py
index 33ba138..63ac418 100644
--- a/erpnext/hr/doctype/driving_license_category/driving_license_category.py
+++ b/erpnext/hr/doctype/driving_license_category/driving_license_category.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class DrivingLicenseCategory(Document):
pass
diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js
index 5639cc9..13b33e2 100755
--- a/erpnext/hr/doctype/employee/employee.js
+++ b/erpnext/hr/doctype/employee/employee.js
@@ -15,19 +15,20 @@
}
refresh() {
- var me = this;
erpnext.toggle_naming_series();
}
date_of_birth() {
return cur_frm.call({
method: "get_retirement_date",
- args: {date_of_birth: this.frm.doc.date_of_birth}
+ args: {
+ date_of_birth: this.frm.doc.date_of_birth
+ }
});
}
salutation() {
- if(this.frm.doc.salutation) {
+ if (this.frm.doc.salutation) {
this.frm.set_value("gender", {
"Mr": "Male",
"Ms": "Female"
@@ -36,8 +37,9 @@
}
};
-frappe.ui.form.on('Employee',{
- setup: function(frm) {
+
+frappe.ui.form.on('Employee', {
+ setup: function (frm) {
frm.set_query("leave_policy", function() {
return {
"filters": {
@@ -46,7 +48,7 @@
};
});
},
- onload:function(frm) {
+ onload: function (frm) {
frm.set_query("department", function() {
return {
"filters": {
@@ -55,23 +57,28 @@
};
});
},
- prefered_contact_email:function(frm){
- frm.events.update_contact(frm)
+ prefered_contact_email: function(frm) {
+ frm.events.update_contact(frm);
},
- personal_email:function(frm){
- frm.events.update_contact(frm)
+
+ personal_email: function(frm) {
+ frm.events.update_contact(frm);
},
- company_email:function(frm){
- frm.events.update_contact(frm)
+
+ company_email: function(frm) {
+ frm.events.update_contact(frm);
},
- user_id:function(frm){
- frm.events.update_contact(frm)
+
+ user_id: function(frm) {
+ frm.events.update_contact(frm);
},
- update_contact:function(frm){
+
+ update_contact: function(frm) {
var prefered_email_fieldname = frappe.model.scrub(frm.doc.prefered_contact_email) || 'user_id';
frm.set_value("prefered_email",
- frm.fields_dict[prefered_email_fieldname].value)
+ frm.fields_dict[prefered_email_fieldname].value);
},
+
status: function(frm) {
return frm.call({
method: "deactivate_sales_person",
@@ -81,19 +88,63 @@
}
});
},
+
create_user: function(frm) {
- if (!frm.doc.prefered_email)
- {
- frappe.throw(__("Please enter Preferred Contact Email"))
+ if (!frm.doc.prefered_email) {
+ frappe.throw(__("Please enter Preferred Contact Email"));
}
frappe.call({
method: "erpnext.hr.doctype.employee.employee.create_user",
- args: { employee: frm.doc.name, email: frm.doc.prefered_email },
- callback: function(r)
- {
- frm.set_value("user_id", r.message)
+ args: {
+ employee: frm.doc.name,
+ email: frm.doc.prefered_email
+ },
+ callback: function (r) {
+ frm.set_value("user_id", r.message);
}
});
}
});
-cur_frm.cscript = new erpnext.hr.EmployeeController({frm: cur_frm});
+
+cur_frm.cscript = new erpnext.hr.EmployeeController({
+ frm: cur_frm
+});
+
+
+frappe.tour['Employee'] = [
+ {
+ fieldname: "first_name",
+ title: "First Name",
+ description: __("Enter First and Last name of Employee, based on Which Full Name will be updated. IN transactions, it will be Full Name which will be fetched.")
+ },
+ {
+ fieldname: "company",
+ title: "Company",
+ description: __("Select a Company this Employee belongs to. Other HR features like Payroll. Expense Claims and Leaves for this Employee will be created for a given company only.")
+ },
+ {
+ fieldname: "date_of_birth",
+ title: "Date of Birth",
+ description: __("Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.")
+ },
+ {
+ fieldname: "date_of_joining",
+ title: "Date of Joining",
+ description: __("Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.")
+ },
+ {
+ fieldname: "holiday_list",
+ title: "Holiday List",
+ description: __("Select a default Holiday List for this Employee. The days listed in Holiday List will not be counted in Leave Application.")
+ },
+ {
+ fieldname: "reports_to",
+ title: "Reports To",
+ description: __("Here, you can select a senior of this Employee. Based on this, Organization Chart will be populated.")
+ },
+ {
+ fieldname: "leave_approver",
+ title: "Leave Approver",
+ description: __("Select Leave Approver for an employee. The user one who will look after his/her Leave application")
+ },
+];
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 5ca4756..79e8f61 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -1,18 +1,21 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
import frappe
-
-from frappe.utils import getdate, validate_email_address, today, add_years, cstr
+from frappe import _, scrub, throw
from frappe.model.naming import set_name_by_naming_series
-from frappe import throw, _, scrub
-from frappe.permissions import add_user_permission, remove_user_permission, \
- set_user_permission_if_allowed, has_permission, get_doc_permissions
-from frappe.model.document import Document
-from erpnext.utilities.transaction_base import delete_events
+from frappe.permissions import (
+ add_user_permission,
+ get_doc_permissions,
+ has_permission,
+ remove_user_permission,
+ set_user_permission_if_allowed,
+)
+from frappe.utils import add_years, cstr, getdate, today, validate_email_address
from frappe.utils.nestedset import NestedSet
+from erpnext.utilities.transaction_base import delete_events
+
+
class EmployeeUserDisabledError(frappe.ValidationError):
pass
class InactiveEmployeeStatusError(frappe.ValidationError):
@@ -286,94 +289,8 @@
employee = frappe.get_doc("Employee", {"user_id": doc.name})
employee.update_user_permissions()
-def send_birthday_reminders():
- """Send Employee birthday reminders if no 'Stop Birthday Reminders' is not set."""
- if int(frappe.db.get_single_value("HR Settings", "stop_birthday_reminders") or 0):
- return
-
- employees_born_today = get_employees_who_are_born_today()
-
- for company, birthday_persons in employees_born_today.items():
- employee_emails = get_all_employee_emails(company)
- birthday_person_emails = [get_employee_email(doc) for doc in birthday_persons]
- recipients = list(set(employee_emails) - set(birthday_person_emails))
-
- reminder_text, message = get_birthday_reminder_text_and_message(birthday_persons)
- send_birthday_reminder(recipients, reminder_text, birthday_persons, message)
-
- if len(birthday_persons) > 1:
- # special email for people sharing birthdays
- for person in birthday_persons:
- person_email = person["user_id"] or person["personal_email"] or person["company_email"]
- others = [d for d in birthday_persons if d != person]
- reminder_text, message = get_birthday_reminder_text_and_message(others)
- send_birthday_reminder(person_email, reminder_text, others, message)
-
def get_employee_email(employee_doc):
- return employee_doc["user_id"] or employee_doc["personal_email"] or employee_doc["company_email"]
-
-def get_birthday_reminder_text_and_message(birthday_persons):
- if len(birthday_persons) == 1:
- 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]
- last_person = person_names[-1]
- birthday_person_text = ", ".join(person_names[:-1])
- birthday_person_text = _("{} & {}").format(birthday_person_text, last_person)
-
- reminder_text = _("Today is {0}'s birthday 🎉").format(birthday_person_text)
- message = _("A friendly reminder of an important date for our team.")
- message += "<br>"
- message += _("Everyone, let’s congratulate {0} on their birthday.").format(birthday_person_text)
-
- return reminder_text, message
-
-def send_birthday_reminder(recipients, reminder_text, birthday_persons, message):
- frappe.sendmail(
- recipients=recipients,
- subject=_("Birthday Reminder"),
- template="birthday_reminder",
- args=dict(
- reminder_text=reminder_text,
- birthday_persons=birthday_persons,
- message=message,
- ),
- header=_("Birthday Reminder 🎂")
- )
-
-def get_employees_who_are_born_today():
- """Get all employee born today & group them based on their company"""
- from collections import defaultdict
- employees_born_today = frappe.db.multisql({
- "mariadb": """
- SELECT `personal_email`, `company`, `company_email`, `user_id`, `employee_name` AS 'name', `image`
- FROM `tabEmployee`
- WHERE
- DAY(date_of_birth) = DAY(%(today)s)
- AND
- MONTH(date_of_birth) = MONTH(%(today)s)
- AND
- `status` = 'Active'
- """,
- "postgres": """
- SELECT "personal_email", "company", "company_email", "user_id", "employee_name" AS 'name', "image"
- FROM "tabEmployee"
- WHERE
- DATE_PART('day', "date_of_birth") = date_part('day', %(today)s)
- AND
- DATE_PART('month', "date_of_birth") = date_part('month', %(today)s)
- AND
- "status" = 'Active'
- """,
- }, dict(today=today()), as_dict=1)
-
- grouped_employees = defaultdict(lambda: [])
-
- for employee_doc in employees_born_today:
- grouped_employees[employee_doc.get('company')].append(employee_doc)
-
- return grouped_employees
+ 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:
@@ -390,17 +307,40 @@
return holiday_list
-def is_holiday(employee, date=None, raise_exception=True):
- '''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'''
+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
+ '''
holiday_list = get_holiday_list_for_employee(employee, raise_exception)
if not date:
date = today()
- if holiday_list:
- return frappe.get_all('Holiday List', dict(name=holiday_list, holiday_date=date)) and True or False
+ if not holiday_list:
+ return False
+
+ filters = {
+ 'parent': holiday_list,
+ 'holiday_date': date
+ }
+ if only_non_weekly:
+ filters['weekly_off'] = False
+
+ 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):
@@ -503,7 +443,6 @@
return employees
-
def on_doctype_update():
frappe.db.add_index("Employee", ["lft", "rgt"])
@@ -520,4 +459,4 @@
user = frappe.session.user
if get_doc_permissions(doc, user=user, ptype=ptype).get(ptype):
return True
- return doc.user_id == user
\ No newline at end of file
+ return doc.user_id == user
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index e853bee..ce307be 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/hr/doctype/employee/employee_reminders.py b/erpnext/hr/doctype/employee/employee_reminders.py
new file mode 100644
index 0000000..216d8f6
--- /dev/null
+++ b/erpnext/hr/doctype/employee/employee_reminders.py
@@ -0,0 +1,249 @@
+# Copyright (c) 2021, 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, add_months, comma_sep, getdate, today
+
+from erpnext.hr.doctype.employee.employee import get_all_employee_emails, get_employee_email
+from erpnext.hr.utils import get_holidays_for_employee
+
+
+# -----------------
+# HOLIDAY REMINDERS
+# -----------------
+def send_reminders_in_advance_weekly():
+ to_send_in_advance = int(frappe.db.get_single_value("HR Settings", "send_holiday_reminders"))
+ frequency = frappe.db.get_single_value("HR Settings", "frequency")
+ if not (to_send_in_advance and frequency == "Weekly"):
+ return
+
+ send_advance_holiday_reminders("Weekly")
+
+def send_reminders_in_advance_monthly():
+ to_send_in_advance = int(frappe.db.get_single_value("HR Settings", "send_holiday_reminders"))
+ frequency = frappe.db.get_single_value("HR Settings", "frequency")
+ if not (to_send_in_advance and frequency == "Monthly"):
+ return
+
+ send_advance_holiday_reminders("Monthly")
+
+def send_advance_holiday_reminders(frequency):
+ """Send Holiday Reminders in Advance to Employees
+ `frequency` (str): 'Weekly' or 'Monthly'
+ """
+ if frequency == "Weekly":
+ start_date = getdate()
+ end_date = add_days(getdate(), 7)
+ elif frequency == "Monthly":
+ # Sent on 1st of every month
+ start_date = getdate()
+ end_date = add_months(getdate(), 1)
+ else:
+ return
+
+ employees = frappe.db.get_all('Employee', pluck='name')
+ for employee in employees:
+ holidays = get_holidays_for_employee(
+ employee,
+ start_date, end_date,
+ only_non_weekly=True,
+ raise_exception=False
+ )
+
+ if not (holidays is None):
+ send_holidays_reminder_in_advance(employee, holidays)
+
+def send_holidays_reminder_in_advance(employee, holidays):
+ employee_doc = frappe.get_doc('Employee', employee)
+ employee_email = get_employee_email(employee_doc)
+ frequency = frappe.db.get_single_value("HR Settings", "frequency")
+
+ email_header = _("Holidays this Month.") if frequency == "Monthly" else _("Holidays this Week.")
+ frappe.sendmail(
+ recipients=[employee_email],
+ 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')),
+ message=_("Below is the list of upcoming holidays for you:"),
+ advance_holiday_reminder=True,
+ holidays=holidays,
+ frequency=frequency[:-2]
+ ),
+ header=email_header
+ )
+
+# ------------------
+# BIRTHDAY REMINDERS
+# ------------------
+def send_birthday_reminders():
+ """Send Employee birthday reminders if no 'Stop Birthday Reminders' is not set."""
+ to_send = int(frappe.db.get_single_value("HR Settings", "send_birthday_reminders"))
+ if not to_send:
+ return
+
+ employees_born_today = get_employees_who_are_born_today()
+
+ for company, birthday_persons in employees_born_today.items():
+ employee_emails = get_all_employee_emails(company)
+ birthday_person_emails = [get_employee_email(doc) for doc in birthday_persons]
+ recipients = list(set(employee_emails) - set(birthday_person_emails))
+
+ reminder_text, message = get_birthday_reminder_text_and_message(birthday_persons)
+ send_birthday_reminder(recipients, reminder_text, birthday_persons, message)
+
+ if len(birthday_persons) > 1:
+ # special email for people sharing birthdays
+ for person in birthday_persons:
+ person_email = person["user_id"] or person["personal_email"] or person["company_email"]
+ others = [d for d in birthday_persons if d != person]
+ reminder_text, message = get_birthday_reminder_text_and_message(others)
+ send_birthday_reminder(person_email, reminder_text, others, message)
+
+def get_birthday_reminder_text_and_message(birthday_persons):
+ if len(birthday_persons) == 1:
+ 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]
+ birthday_person_text = comma_sep(person_names, frappe._("{0} & {1}"), False)
+
+ reminder_text = _("Today is {0}'s birthday 🎉").format(birthday_person_text)
+ message = _("A friendly reminder of an important date for our team.")
+ message += "<br>"
+ message += _("Everyone, let’s congratulate {0} on their birthday.").format(birthday_person_text)
+
+ return reminder_text, message
+
+def send_birthday_reminder(recipients, reminder_text, birthday_persons, message):
+ frappe.sendmail(
+ recipients=recipients,
+ subject=_("Birthday Reminder"),
+ template="birthday_reminder",
+ args=dict(
+ reminder_text=reminder_text,
+ birthday_persons=birthday_persons,
+ message=message,
+ ),
+ header=_("Birthday Reminder 🎂")
+ )
+
+def get_employees_who_are_born_today():
+ """Get all employee born today & group them based on their company"""
+ return get_employees_having_an_event_today("birthday")
+
+def get_employees_having_an_event_today(event_type):
+ """Get all employee who have `event_type` today
+ & group them based on their company. `event_type`
+ can be `birthday` or `work_anniversary`"""
+
+ 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'
+ else:
+ return
+
+ 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
+ DAY({condition_column}) = DAY(%(today)s)
+ AND
+ MONTH({condition_column}) = MONTH(%(today)s)
+ AND
+ `status` = 'Active'
+ """,
+ "postgres": f"""
+ SELECT "personal_email", "company", "company_email", "user_id", "employee_name" AS 'name', "image"
+ FROM "tabEmployee"
+ WHERE
+ DATE_PART('day', {condition_column}) = date_part('day', %(today)s)
+ AND
+ DATE_PART('month', {condition_column}) = date_part('month', %(today)s)
+ AND
+ "status" = 'Active'
+ """,
+ }, 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)
+
+ return grouped_employees
+
+
+# --------------------------
+# WORK ANNIVERSARY REMINDERS
+# --------------------------
+def send_work_anniversary_reminders():
+ """Send Employee Work Anniversary Reminders if 'Send Work Anniversary Reminders' is checked"""
+ to_send = int(frappe.db.get_single_value("HR Settings", "send_work_anniversary_reminders"))
+ if not to_send:
+ return
+
+ employees_joined_today = get_employees_having_an_event_today("work_anniversary")
+
+ for company, anniversary_persons in employees_joined_today.items():
+ employee_emails = get_all_employee_emails(company)
+ anniversary_person_emails = [get_employee_email(doc) for doc in anniversary_persons]
+ recipients = list(set(employee_emails) - set(anniversary_person_emails))
+
+ reminder_text, message = get_work_anniversary_reminder_text_and_message(anniversary_persons)
+ send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message)
+
+ if len(anniversary_persons) > 1:
+ # email for people sharing work anniversaries
+ for person in anniversary_persons:
+ person_email = person["user_id"] or person["personal_email"] or person["company_email"]
+ others = [d for d in anniversary_persons if d != person]
+ reminder_text, message = get_work_anniversary_reminder_text_and_message(others)
+ send_work_anniversary_reminder(person_email, reminder_text, others, message)
+
+def get_work_anniversary_reminder_text_and_message(anniversary_persons):
+ if len(anniversary_persons) == 1:
+ 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} years"
+ else:
+ person_names_with_years = []
+ names = []
+ for person in anniversary_persons:
+ 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} years"
+ person_names_with_years.append(person_text)
+
+ # converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim
+ anniversary_person = comma_sep(person_names_with_years, frappe._("{0} & {1}"), False)
+ persons_name = comma_sep(names, frappe._("{0} & {1}"), False)
+
+ reminder_text = _("Today {0} at our Company! 🎉").format(anniversary_person)
+ message = _("A friendly reminder of an important date for our team.")
+ message += "<br>"
+ message += _("Everyone, let’s congratulate {0} on their work anniversary!").format(persons_name)
+
+ return reminder_text, message
+
+def send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message):
+ frappe.sendmail(
+ recipients=recipients,
+ subject=_("Work Anniversary Reminder"),
+ template="anniversary_reminder",
+ args=dict(
+ reminder_text=reminder_text,
+ anniversary_persons=anniversary_persons,
+ message=message,
+ ),
+ header=_("🎊️🎊️ Work Anniversary Reminder 🎊️🎊️")
+ )
diff --git a/erpnext/hr/doctype/employee/employee_tree.js b/erpnext/hr/doctype/employee/employee_tree.js
index 9ab091a..7d6a700 100644
--- a/erpnext/hr/doctype/employee/employee_tree.js
+++ b/erpnext/hr/doctype/employee/employee_tree.js
@@ -33,4 +33,4 @@
condition: 'frappe.boot.user.can_create.indexOf("Employee") !== -1'
}
],
-};
\ No newline at end of file
+};
diff --git a/erpnext/hr/doctype/employee/test_employee.js b/erpnext/hr/doctype/employee/test_employee.js
index 200dcd7..3a41458 100644
--- a/erpnext/hr/doctype/employee/test_employee.js
+++ b/erpnext/hr/doctype/employee/test_employee.js
@@ -37,4 +37,4 @@
() => frappe.timeout(10),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index 8fc7cf1..8d6dfa2 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -1,40 +1,17 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
+import unittest
import frappe
-import erpnext
-import unittest
import frappe.utils
+
+import erpnext
from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records('Employee')
class TestEmployee(unittest.TestCase):
- def test_birthday_reminders(self):
- 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"
- employee.save()
-
- from erpnext.hr.doctype.employee.employee import get_employees_who_are_born_today, send_birthday_reminders
-
- employees_born_today = get_employees_who_are_born_today()
- self.assertTrue(employees_born_today.get("_Test Company"))
-
- frappe.db.sql("delete from `tabEmail Queue`")
-
- hr_settings = frappe.get_doc("HR Settings", "HR Settings")
- hr_settings.stop_birthday_reminders = 0
- hr_settings.save()
-
- send_birthday_reminders()
-
- email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
- self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message)
-
def test_employee_status_left(self):
employee1 = make_employee("test_employee_1@company.com")
employee2 = make_employee("test_employee_2@company.com")
@@ -48,9 +25,9 @@
self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
def test_employee_status_inactive(self):
- from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
- from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
+ 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
employee = make_employee("test_employee_status@company.com")
employee_doc = frappe.get_doc("Employee", employee)
diff --git a/erpnext/hr/doctype/employee/test_employee_reminders.py b/erpnext/hr/doctype/employee/test_employee_reminders.py
new file mode 100644
index 0000000..52c0098
--- /dev/null
+++ b/erpnext/hr/doctype/employee/test_employee_reminders.py
@@ -0,0 +1,182 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import unittest
+from datetime import timedelta
+
+import frappe
+from frappe.utils import getdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.hr_settings.hr_settings import set_proceed_with_frequency_change
+
+
+class TestEmployeeReminders(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ from erpnext.hr.doctype.holiday_list.test_holiday_list import make_holiday_list
+
+ # Create a test holiday list
+ test_holiday_dates = cls.get_test_holiday_dates()
+ test_holiday_list = make_holiday_list(
+ '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'},
+ ],
+ 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")
+ )
+
+ # Attach the holiday list to employee
+ test_employee.holiday_list = test_holiday_list.name
+ test_employee.save()
+
+ # Attach to class
+ cls.test_employee = test_employee
+ cls.test_holiday_dates = test_holiday_dates
+
+ @classmethod
+ def get_test_holiday_dates(cls):
+ 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)
+ ]
+
+ def setUp(self):
+ # Clear Email Queue
+ frappe.db.sql("delete from `tabEmail Queue`")
+
+ def test_is_holiday(self):
+ from erpnext.hr.doctype.employee.employee import is_holiday
+
+ 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)))
+
+ # 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))
+
+ # 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)
+
+ def test_birthday_reminders(self):
+ 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"
+ employee.save()
+
+ from erpnext.hr.doctype.employee.employee_reminders import (
+ get_employees_who_are_born_today,
+ send_birthday_reminders,
+ )
+
+ employees_born_today = get_employees_who_are_born_today()
+ self.assertTrue(employees_born_today.get("_Test Company"))
+
+ hr_settings = frappe.get_doc("HR Settings", "HR Settings")
+ hr_settings.send_birthday_reminders = 1
+ hr_settings.save()
+
+ send_birthday_reminders()
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message)
+
+ def test_work_anniversary_reminders(self):
+ employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
+ employee.date_of_joining = "1998" + frappe.utils.nowdate()[4:]
+ employee.company_email = "test@example.com"
+ employee.company = "_Test Company"
+ employee.save()
+
+ from erpnext.hr.doctype.employee.employee_reminders import (
+ get_employees_having_an_event_today,
+ send_work_anniversary_reminders,
+ )
+
+ employees_having_work_anniversary = get_employees_having_an_event_today('work_anniversary')
+ self.assertTrue(employees_having_work_anniversary.get("_Test Company"))
+
+ hr_settings = frappe.get_doc("HR Settings", "HR Settings")
+ hr_settings.send_work_anniversary_reminders = 1
+ hr_settings.save()
+
+ send_work_anniversary_reminders()
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertTrue("Subject: Work Anniversary Reminder" in email_queue[0].message)
+
+ def test_send_holidays_reminder_in_advance(self):
+ from erpnext.hr.doctype.employee.employee_reminders import send_holidays_reminder_in_advance
+ from erpnext.hr.utils import get_holidays_for_employee
+
+ # Get HR settings and enable advance holiday reminders
+ hr_settings = frappe.get_doc("HR Settings", "HR Settings")
+ hr_settings.send_holiday_reminders = 1
+ set_proceed_with_frequency_change()
+ hr_settings.frequency = 'Weekly'
+ hr_settings.save()
+
+ 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
+ )
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertEqual(len(email_queue), 1)
+
+ def test_advance_holiday_reminders_monthly(self):
+ from erpnext.hr.doctype.employee.employee_reminders import send_reminders_in_advance_monthly
+
+ # Get HR settings and enable advance holiday reminders
+ hr_settings = frappe.get_doc("HR Settings", "HR Settings")
+ hr_settings.send_holiday_reminders = 1
+ set_proceed_with_frequency_change()
+ hr_settings.frequency = 'Monthly'
+ hr_settings.save()
+
+ send_reminders_in_advance_monthly()
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertTrue(len(email_queue) > 0)
+
+ def test_advance_holiday_reminders_weekly(self):
+ from erpnext.hr.doctype.employee.employee_reminders import send_reminders_in_advance_weekly
+
+ # Get HR settings and enable advance holiday reminders
+ hr_settings = frappe.get_doc("HR Settings", "HR Settings")
+ hr_settings.send_holiday_reminders = 1
+ hr_settings.frequency = 'Weekly'
+ hr_settings.save()
+
+ send_reminders_in_advance_weekly()
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertTrue(len(email_queue) > 0)
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index fa4b06a..7d1c7cb 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -73,7 +73,7 @@
frm.trigger('make_return_entry');
}, __('Create'));
} else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) {
- frm.add_custom_button(__("Deduction from salary"), function() {
+ frm.add_custom_button(__("Deduction from Salary"), function() {
frm.events.make_deduction_via_additional_salary(frm);
}, __('Create'));
}
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json
index ea25aa7..0475453 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.json
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.json
@@ -170,7 +170,7 @@
"default": "0",
"fieldname": "repay_unclaimed_amount_from_salary",
"fieldtype": "Check",
- "label": "Repay unclaimed amount from salary"
+ "label": "Repay Unclaimed Amount from Salary"
},
{
"depends_on": "eval:cur_frm.doc.employee",
@@ -200,10 +200,11 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2021-03-31 22:31:53.746659",
+ "modified": "2021-09-11 18:38:38.617478",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py
index cbb3cc8..8d90bcc 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.py
@@ -3,13 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import flt, nowdate
+
+import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.hr.utils import validate_active_employee
+
class EmployeeAdvanceOverPayment(frappe.ValidationError):
pass
@@ -168,7 +172,10 @@
@frappe.whitelist()
def create_return_through_additional_salary(doc):
import json
- doc = frappe._dict(json.loads(doc))
+
+ if isinstance(doc, str):
+ doc = frappe._dict(json.loads(doc))
+
additional_salary = frappe.new_doc('Additional Salary')
additional_salary.employee = doc.employee
additional_salary.currency = doc.currency
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
index 2f493e2..17d5bd2 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.js b/erpnext/hr/doctype/employee_advance/test_employee_advance.js
deleted file mode 100644
index 1b9ec6f..0000000
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Advance", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Advance
- () => frappe.tests.make('Employee Advance', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
index c88b2b8..c439d45 100644
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
@@ -3,12 +3,21 @@
# See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
import unittest
+
+import frappe
from frappe.utils import nowdate
-from erpnext.hr.doctype.employee_advance.employee_advance import make_bank_entry
-from erpnext.hr.doctype.employee_advance.employee_advance import EmployeeAdvanceOverPayment
+
+import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.employee_advance.employee_advance import (
+ EmployeeAdvanceOverPayment,
+ create_return_through_additional_salary,
+ make_bank_entry,
+)
+from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
class TestEmployeeAdvance(unittest.TestCase):
def test_paid_amount_and_status(self):
@@ -27,6 +36,46 @@
journal_entry1 = make_payment_entry(advance)
self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit)
+ def test_repay_unclaimed_amount_from_salary(self):
+ employee_name = make_employee("_T@employe.advance")
+ advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1})
+
+ args = {"type": "Deduction"}
+ create_salary_component("Advance Salary - Deduction", **args)
+ make_salary_structure("Test Additional Salary for Advance Return", "Monthly", employee=employee_name)
+
+ # additional salary for 700 first
+ advance.reload()
+ additional_salary = create_return_through_additional_salary(advance)
+ additional_salary.salary_component = "Advance Salary - Deduction"
+ additional_salary.payroll_date = nowdate()
+ additional_salary.amount = 700
+ additional_salary.insert()
+ additional_salary.submit()
+
+ advance.reload()
+ self.assertEqual(advance.return_amount, 700)
+
+ # additional salary for remaining 300
+ additional_salary = create_return_through_additional_salary(advance)
+ additional_salary.salary_component = "Advance Salary - Deduction"
+ additional_salary.payroll_date = nowdate()
+ additional_salary.amount = 300
+ additional_salary.insert()
+ additional_salary.submit()
+
+ advance.reload()
+ self.assertEqual(advance.return_amount, 1000)
+
+ # update advance return amount on additional salary cancellation
+ additional_salary.cancel()
+ advance.reload()
+ self.assertEqual(advance.return_amount, 700)
+
+ def tearDown(self):
+ frappe.db.rollback()
+
+
def make_payment_entry(advance):
journal_entry = frappe.get_doc(make_bank_entry("Employee Advance", advance.name))
journal_entry.cheque_no = "123123"
@@ -35,7 +84,7 @@
return journal_entry
-def make_employee_advance(employee_name):
+def make_employee_advance(employee_name, args=None):
doc = frappe.new_doc("Employee Advance")
doc.employee = employee_name
doc.company = "_Test company"
@@ -45,7 +94,11 @@
doc.advance_amount = 1000
doc.posting_date = nowdate()
doc.advance_account = "_Test Employee Advance - _TC"
+
+ if args:
+ doc.update(args)
+
doc.insert()
doc.submit()
- return doc
\ No newline at end of file
+ return doc
diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css
index d25fb22..c8d6644 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css
@@ -18,4 +18,4 @@
.checkbox{
margin-top: -3px;
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js
index ab965d5..5ae8c6b 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js
@@ -267,5 +267,3 @@
mark_employee_toolbar.appendTo($(this.wrapper));
}
};
-
-
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 16c1a32..1a1bcb2 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
+++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
from frappe.model.document import Document
from frappe.utils import getdate
@@ -53,8 +55,7 @@
else:
leave_type = None
- if not company:
- company = frappe.db.get_value("Employee", employee['employee'], "Company")
+ company = frappe.db.get_value("Employee", employee['employee'], "Company", cache=True)
attendance=frappe.get_doc(dict(
doctype='Attendance',
@@ -66,4 +67,4 @@
company=company
))
attendance.insert()
- attendance.submit()
+ attendance.submit()
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js
index 2827d4b..48d4344 100644
--- a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js
+++ b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js
@@ -58,4 +58,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json
index 65792b4..044a5a9 100644
--- a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json
+++ b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2018-05-09 05:37:18.439763",
"doctype": "DocType",
"editable_grid": 1,
@@ -7,6 +8,8 @@
"activity_name",
"user",
"role",
+ "begin_on",
+ "duration",
"column_break_3",
"task",
"task_weight",
@@ -16,12 +19,16 @@
],
"fields": [
{
+ "columns": 3,
"fieldname": "activity_name",
"fieldtype": "Data",
"in_list_view": 1,
- "label": "Activity Name"
+ "label": "Activity Name",
+ "reqd": 1
},
{
+ "columns": 2,
+ "depends_on": "eval:!doc.role",
"fieldname": "user",
"fieldtype": "Link",
"in_list_view": 1,
@@ -29,9 +36,10 @@
"options": "User"
},
{
+ "columns": 1,
+ "depends_on": "eval:!doc.user",
"fieldname": "role",
"fieldtype": "Link",
- "in_list_view": 1,
"label": "Role",
"options": "Role"
},
@@ -67,10 +75,25 @@
"fieldname": "description",
"fieldtype": "Text Editor",
"label": "Description"
+ },
+ {
+ "columns": 2,
+ "fieldname": "duration",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Duration (Days)"
+ },
+ {
+ "columns": 2,
+ "fieldname": "begin_on",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Begin On (Days)"
}
],
"istable": 1,
- "modified": "2019-06-03 19:22:42.965762",
+ "links": [],
+ "modified": "2021-07-30 15:55:22.470102",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Boarding Activity",
diff --git a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py
index 496f165..48c85f4 100644
--- a/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py
+++ b/erpnext/hr/doctype/employee_boarding_activity/employee_boarding_activity.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeBoardingActivity(Document):
pass
diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
index 60ea0f9..1ae9b1f 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
@@ -3,14 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import now, cint, get_datetime
-from frappe.model.document import Document
-from frappe import _
-from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import cint, get_datetime
+
+from erpnext.hr.doctype.shift_assignment.shift_assignment import (
+ get_actual_start_end_datetime_of_shift,
+)
from erpnext.hr.utils import validate_active_employee
+
class EmployeeCheckin(Document):
def validate(self):
validate_active_employee(self.employee)
@@ -176,4 +180,3 @@
def find_index_in_dict(dict_list, key, value):
return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
-
diff --git a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
index 9f12ef2..71c6498 100644
--- a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py
@@ -3,13 +3,19 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import now_datetime, nowdate, to_timedelta
import unittest
from datetime import timedelta
-from erpnext.hr.doctype.employee_checkin.employee_checkin import add_log_based_on_employee_field, mark_attendance_and_link_log, calculate_working_hours
+import frappe
+from frappe.utils import now_datetime, nowdate
+
from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.employee_checkin.employee_checkin import (
+ add_log_based_on_employee_field,
+ calculate_working_hours,
+ mark_attendance_and_link_log,
+)
+
class TestEmployeeCheckin(unittest.TestCase):
def test_add_log_based_on_employee_field(self):
@@ -42,11 +48,11 @@
self.assertEqual(logs_count, 4)
attendance_count = frappe.db.count('Attendance', {'status':'Present', 'working_hours':8.2,
'employee':employee, 'attendance_date':now_date})
- self.assertEqual(attendance_count, 1)
+ self.assertEqual(attendance_count, 1)
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']
+ '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 = [
diff --git a/erpnext/hr/doctype/employee_education/employee_education.py b/erpnext/hr/doctype/employee_education/employee_education.py
index a1d4492..cadf5d6 100644
--- a/erpnext/hr/doctype/employee_education/employee_education.py
+++ b/erpnext/hr/doctype/employee_education/employee_education.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class EmployeeEducation(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py b/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py
index c716630..4d0e8d9 100644
--- a/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py
+++ b/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class EmployeeExternalWorkHistory(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.py b/erpnext/hr/doctype/employee_grade/employee_grade.py
index 42a9f16..b097038 100644
--- a/erpnext/hr/doctype/employee_grade/employee_grade.py
+++ b/erpnext/hr/doctype/employee_grade/employee_grade.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeGrade(Document):
pass
diff --git a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
index f2656e9..92d9fa0 100644
--- a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
+++ b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'transactions': [
@@ -10,4 +11,4 @@
'items': ['Employee Onboarding Template', 'Employee Separation Template']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/employee_grade/test_employee_grade.js b/erpnext/hr/doctype/employee_grade/test_employee_grade.js
deleted file mode 100644
index d684fb2..0000000
--- a/erpnext/hr/doctype/employee_grade/test_employee_grade.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Grade", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Grade
- () => frappe.tests.make('Employee Grade', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_grade/test_employee_grade.py b/erpnext/hr/doctype/employee_grade/test_employee_grade.py
index 93058cf..cd4fcb5 100644
--- a/erpnext/hr/doctype/employee_grade/test_employee_grade.py
+++ b/erpnext/hr/doctype/employee_grade/test_employee_grade.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeGrade(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.py b/erpnext/hr/doctype/employee_grievance/employee_grievance.py
index 503b5ea..fd9a33b 100644
--- a/erpnext/hr/doctype/employee_grievance/employee_grievance.py
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.py
@@ -5,6 +5,7 @@
from frappe import _, bold
from frappe.model.document import Document
+
class EmployeeGrievance(Document):
def on_submit(self):
if self.status not in ["Invalid", "Resolved"]:
@@ -12,4 +13,3 @@
bold("Invalid"),
bold("Resolved"))
)
-
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js
index fc08e21..11672ca 100644
--- a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js
@@ -9,4 +9,4 @@
};
return [__(doc.status), colors[doc.status], "status,=," + doc.status];
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
index a615b20..e2d0002 100644
--- a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
+++ b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
@@ -1,10 +1,14 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-import frappe
import unittest
+
+import frappe
from frappe.utils import today
+
from erpnext.hr.doctype.employee.test_employee import make_employee
+
+
class TestEmployeeGrievance(unittest.TestCase):
def test_create_employee_grievance(self):
create_employee_grievance()
@@ -48,4 +52,3 @@
grievance_type.save()
return grievance_type.name
-
diff --git a/erpnext/hr/doctype/employee_group/employee_group.py b/erpnext/hr/doctype/employee_group/employee_group.py
index 3025877..b2fe5eb 100644
--- a/erpnext/hr/doctype/employee_group/employee_group.py
+++ b/erpnext/hr/doctype/employee_group/employee_group.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class EmployeeGroup(Document):
pass
diff --git a/erpnext/hr/doctype/employee_group/test_employee_group.py b/erpnext/hr/doctype/employee_group/test_employee_group.py
index 3a6bf85..053e840 100644
--- a/erpnext/hr/doctype/employee_group/test_employee_group.py
+++ b/erpnext/hr/doctype/employee_group/test_employee_group.py
@@ -2,10 +2,14 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
+
+import frappe
+
from erpnext.hr.doctype.employee.test_employee import make_employee
+
class TestEmployeeGroup(unittest.TestCase):
pass
@@ -29,4 +33,4 @@
def get_employee_group():
employee_group = frappe.db.exists("Employee Group", "_Test Employee Group")
- return employee_group
\ No newline at end of file
+ return employee_group
diff --git a/erpnext/hr/doctype/employee_group_table/employee_group_table.py b/erpnext/hr/doctype/employee_group_table/employee_group_table.py
index 816611d..d9407a9 100644
--- a/erpnext/hr/doctype/employee_group_table/employee_group_table.py
+++ b/erpnext/hr/doctype/employee_group_table/employee_group_table.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class EmployeeGroupTable(Document):
pass
diff --git a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py b/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py
index abc01ef..4f2d1a0 100644
--- a/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py
+++ b/erpnext/hr/doctype/employee_health_insurance/employee_health_insurance.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeHealthInsurance(Document):
pass
diff --git a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.js b/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.js
deleted file mode 100644
index 245cb32..0000000
--- a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Health Insurance", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Health Insurance
- () => frappe.tests.make('Employee Health Insurance', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py b/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py
index f0787f5..38e3ee3 100644
--- a/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py
+++ b/erpnext/hr/doctype/employee_health_insurance/test_employee_health_insurance.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeHealthInsurance(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py b/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py
index d0f3d8d..6076abb 100644
--- a/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py
+++ b/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class EmployeeInternalWorkHistory(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
index 673e228..fd877a6 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json
@@ -8,20 +8,24 @@
"field_order": [
"job_applicant",
"job_offer",
- "employee_name",
- "employee",
- "date_of_joining",
- "boarding_status",
- "notify_users_by_email",
- "column_break_7",
"employee_onboarding_template",
+ "column_break_7",
"company",
+ "boarding_status",
+ "project",
+ "details_section",
+ "employee",
+ "employee_name",
"department",
"designation",
"employee_grade",
- "project",
+ "holiday_list",
+ "column_break_13",
+ "date_of_joining",
+ "boarding_begins_on",
"table_for_activity",
"activities",
+ "notify_users_by_email",
"amended_from"
],
"fields": [
@@ -58,7 +62,8 @@
"fieldname": "date_of_joining",
"fieldtype": "Date",
"in_list_view": 1,
- "label": "Date of Joining"
+ "label": "Date of Joining",
+ "reqd": 1
},
{
"allow_on_submit": 1,
@@ -90,7 +95,8 @@
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
- "options": "Company"
+ "options": "Company",
+ "reqd": 1
},
{
"fieldname": "department",
@@ -121,7 +127,8 @@
},
{
"fieldname": "table_for_activity",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Onboarding Activities"
},
{
"allow_on_submit": 1,
@@ -138,11 +145,32 @@
"options": "Employee Onboarding",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "details_section",
+ "fieldtype": "Section Break",
+ "label": "Employee Details"
+ },
+ {
+ "fieldname": "column_break_13",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "boarding_begins_on",
+ "fieldtype": "Date",
+ "label": "Onboarding Begins On",
+ "reqd": 1
+ },
+ {
+ "fieldname": "holiday_list",
+ "fieldtype": "Link",
+ "label": "Holiday List",
+ "options": "Holiday List"
}
],
"is_submittable": 1,
"links": [],
- "modified": "2021-06-03 18:01:51.097927",
+ "modified": "2021-07-30 14:55:04.560683",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Onboarding",
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
index 55fe317..e96447b 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController
from frappe.model.mapper import get_mapped_doc
+from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController
+
+
class IncompleteTaskError(frappe.ValidationError): pass
class EmployeeOnboarding(EmployeeBoardingController):
@@ -57,4 +60,3 @@
}}
}, target_doc, set_missing_values)
return doc
-
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.js
deleted file mode 100644
index d15cef7..0000000
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Onboarding", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Onboarding
- () => frappe.tests.make('Employee Onboarding', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index 5f7756b..1e3b9cb 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -3,12 +3,18 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate
-from erpnext.hr.doctype.employee_onboarding.employee_onboarding import make_employee
-from erpnext.hr.doctype.employee_onboarding.employee_onboarding import IncompleteTaskError
+
+import frappe
+from frappe.utils import getdate
+
+from erpnext.hr.doctype.employee_onboarding.employee_onboarding import (
+ IncompleteTaskError,
+ make_employee,
+)
from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
+
class TestEmployeeOnboarding(unittest.TestCase):
def setUp(self):
@@ -46,7 +52,7 @@
onboarding.reload()
employee = make_employee(onboarding.name)
employee.first_name = employee.employee_name
- employee.date_of_joining = nowdate()
+ employee.date_of_joining = getdate()
employee.date_of_birth = '1990-05-08'
employee.gender = 'Female'
employee.insert()
@@ -65,6 +71,7 @@
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()
@@ -82,11 +89,14 @@
def create_employee_onboarding():
applicant = get_job_applicant()
job_offer = get_job_offer(applicant.name)
+ holiday_list = make_holiday_list()
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.holiday_list = holiday_list
onboarding.designation = 'Researcher'
onboarding.append('activities', {
'activity_name': 'Assign ID Card',
@@ -101,4 +111,4 @@
onboarding.insert()
onboarding.submit()
- return onboarding
\ No newline at end of file
+ return onboarding
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.json b/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.json
deleted file mode 100644
index 4e91b72..0000000
--- a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.json
+++ /dev/null
@@ -1,290 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-05-09 05:37:18.439763",
- "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": "activity_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": "Activity 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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "User",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "role",
- "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": "Role",
- "length": 0,
- "no_copy": 0,
- "options": "Role",
- "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": "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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval: doc.parenttype == \"Employee Onboarding\"",
- "fieldname": "completed",
- "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": "Completed",
- "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": "required_for_employee_creation",
- "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": "Required for Employee Creation",
- "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": "section_break_6",
- "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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "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": "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": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-05-09 06:15:41.768236",
- "modified_by": "Administrator",
- "module": "HR",
- "name": "Employee Onboarding 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
-}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.py b/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.py
deleted file mode 100644
index d170631..0000000
--- a/erpnext/hr/doctype/employee_onboarding_activity/employee_onboarding_activity.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class EmployeeOnboardingActivity(Document):
- pass
diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py
index 6f1c316..a46b3cd 100644
--- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py
+++ b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeOnboardingTemplate(Document):
pass
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 837da53..1d2e8ae 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,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Employee Onboarding']
},
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.js b/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.js
deleted file mode 100644
index 10912ed..0000000
--- a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Onboarding Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Onboarding Template
- () => frappe.tests.make('Employee Onboarding Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py b/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py
index f4b5b88..92a328b 100644
--- a/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py
+++ b/erpnext/hr/doctype/employee_onboarding_template/test_employee_onboarding_template.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeOnboardingTemplate(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index a3a6183..164d48b 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
+
from erpnext.hr.utils import update_employee, validate_active_employee
+
class EmployeePromotion(Document):
def validate(self):
validate_active_employee(self.employee)
diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.js b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.js
deleted file mode 100644
index 5f0a5ba..0000000
--- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Promotion", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Promotion
- () => frappe.tests.make('Employee Promotion', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
index 9e7d318..39af6ff 100644
--- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py
@@ -3,11 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import getdate, add_days
+
+import frappe
+from frappe.utils import add_days, getdate
+
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee
+
class TestEmployeePromotion(unittest.TestCase):
def setUp(self):
self.employee = make_employee("employee@promotions.com")
diff --git a/erpnext/hr/doctype/employee_property_history/employee_property_history.py b/erpnext/hr/doctype/employee_property_history/employee_property_history.py
index fb67852..9e25492 100644
--- a/erpnext/hr/doctype/employee_property_history/employee_property_history.py
+++ b/erpnext/hr/doctype/employee_property_history/employee_property_history.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeePropertyHistory(Document):
pass
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.js b/erpnext/hr/doctype/employee_referral/employee_referral.js
index 9c99bbb..8722019 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral.js
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.js
@@ -43,8 +43,6 @@
});
}
-
-
},
create_job_applicant: function(frm) {
frappe.model.open_mapped_doc({
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py
index 0493306..db356bf 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import get_link_to_form
from frappe.model.document import Document
+from frappe.utils import get_link_to_form
+
from erpnext.hr.utils import validate_active_employee
+
class EmployeeReferral(Document):
def validate(self):
validate_active_employee(self.referrer)
@@ -35,8 +38,10 @@
status = "Open"
job_applicant = frappe.new_doc("Job Applicant")
+ job_applicant.source = "Employee Referral"
job_applicant.employee_referral = emp_ref.name
job_applicant.status = status
+ job_applicant.designation = emp_ref.for_designation
job_applicant.applicant_name = emp_ref.full_name
job_applicant.email_id = emp_ref.email
job_applicant.phone_number = emp_ref.contact_no
@@ -56,6 +61,7 @@
@frappe.whitelist()
def create_additional_salary(doc):
import json
+
from six import string_types
if isinstance(doc, string_types):
@@ -70,4 +76,3 @@
additional_salary.ref_docname = doc.name
return additional_salary
-
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
index afa2a1f..85d6c20 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'employee_referral',
@@ -12,4 +13,4 @@
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_list.js b/erpnext/hr/doctype/employee_referral/employee_referral_list.js
index 7533ab6..38dfc4d 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral_list.js
+++ b/erpnext/hr/doctype/employee_referral/employee_referral_list.js
@@ -11,4 +11,4 @@
return [__(doc.status), "red", "status,=," + doc.status];
}
},
-};
\ No newline at end of file
+};
diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
index a674f39..1340f62 100644
--- a/erpnext/hr/doctype/employee_referral/test_employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
@@ -3,14 +3,25 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import today
-from erpnext.hr.doctype.designation.test_designation import create_designation
-from erpnext.hr.doctype.employee_referral.employee_referral import create_job_applicant, create_additional_salary
-from erpnext.hr.doctype.employee.test_employee import make_employee
import unittest
+import frappe
+from frappe.utils import today
+
+from erpnext.hr.doctype.designation.test_designation import create_designation
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.employee_referral.employee_referral import (
+ create_additional_salary,
+ create_job_applicant,
+)
+
+
class TestEmployeeReferral(unittest.TestCase):
+
+ def setUp(self):
+ frappe.db.sql("DELETE FROM `tabJob Applicant`")
+ frappe.db.sql("DELETE FROM `tabEmployee Referral`")
+
def test_workflow_and_status_sync(self):
emp_ref = create_employee_referral()
@@ -44,6 +55,10 @@
add_sal = create_additional_salary(emp_ref)
self.assertTrue(add_sal.ref_docname, emp_ref.name)
+ def tearDown(self):
+ frappe.db.sql("DELETE FROM `tabJob Applicant`")
+ frappe.db.sql("DELETE FROM `tabEmployee Referral`")
+
def create_employee_referral():
emp_ref = frappe.new_doc("Employee Referral")
@@ -57,4 +72,4 @@
emp_ref.save()
emp_ref.submit()
- return emp_ref
\ No newline at end of file
+ return emp_ref
diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.json b/erpnext/hr/doctype/employee_separation/employee_separation.json
index c10da5c..c240493 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.json
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.json
@@ -15,6 +15,7 @@
"company",
"boarding_status",
"resignation_letter_date",
+ "boarding_begins_on",
"project",
"table_for_activity",
"employee_separation_template",
@@ -144,11 +145,17 @@
"options": "Employee Separation",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "boarding_begins_on",
+ "fieldtype": "Date",
+ "label": "Separation Begins On",
+ "reqd": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2021-06-03 18:02:54.007313",
+ "modified": "2021-07-30 14:03:51.218791",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Separation",
diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.py b/erpnext/hr/doctype/employee_separation/employee_separation.py
index 8afee25..ad279e8 100644
--- a/erpnext/hr/doctype/employee_separation/employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/employee_separation.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController
+
class EmployeeSeparation(EmployeeBoardingController):
def validate(self):
super(EmployeeSeparation, self).validate()
diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.js b/erpnext/hr/doctype/employee_separation/test_employee_separation.js
deleted file mode 100644
index d6c6359..0000000
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Separation", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Separation
- () => frappe.tests.make('Employee Separation', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
index f787d9c..c7068dd 100644
--- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py
+++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+from frappe.utils import getdate
+
test_dependencies = ['Employee Onboarding']
class TestEmployeeSeparation(unittest.TestCase):
@@ -34,9 +36,10 @@
doc.delete()
def create_employee_separation():
- employee = frappe.db.get_value('Employee', {'status': 'Active'})
+ 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',
@@ -45,4 +48,4 @@
separation.boarding_status = 'Pending'
separation.insert()
separation.submit()
- return separation
\ No newline at end of file
+ return separation
diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py b/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py
index 0508fc4..7a263dc 100644
--- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py
+++ b/erpnext/hr/doctype/employee_separation_template/employee_separation_template.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeSeparationTemplate(Document):
pass
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 39345f0..970ba26 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,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Employee Separation']
},
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.js b/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.js
deleted file mode 100644
index 66fd450..0000000
--- a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Separation Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Separation Template
- () => frappe.tests.make('Employee Separation Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py b/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py
index 3fd3d39..4c91a79 100644
--- a/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py
+++ b/erpnext/hr/doctype/employee_separation_template/test_employee_separation_template.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeSeparationTemplate(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employee_skill/employee_skill.py b/erpnext/hr/doctype/employee_skill/employee_skill.py
index ac05fba..6f860c6 100644
--- a/erpnext/hr/doctype/employee_skill/employee_skill.py
+++ b/erpnext/hr/doctype/employee_skill/employee_skill.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeSkill(Document):
pass
diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py
index 073f02f..d93c22f 100644
--- a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py
+++ b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeSkillMap(Document):
pass
diff --git a/erpnext/hr/doctype/employee_training/employee_training.py b/erpnext/hr/doctype/employee_training/employee_training.py
index 810796d..068116a 100644
--- a/erpnext/hr/doctype/employee_training/employee_training.py
+++ b/erpnext/hr/doctype/employee_training/employee_training.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeTraining(Document):
pass
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index c200774..b1f6609 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
+
from erpnext.hr.utils import update_employee
+
class EmployeeTransfer(Document):
def before_submit(self):
if getdate(self.transfer_date) > getdate():
diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.js b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.js
deleted file mode 100644
index 05a3e1a..0000000
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Transfer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Transfer
- () => frappe.tests.make('Employee Transfer', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
index 93fc7a2..ad2f3ad 100644
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
@@ -3,11 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import getdate, add_days
+
+import frappe
+from frappe.utils import add_days, getdate
+
from erpnext.hr.doctype.employee.test_employee import make_employee
+
class TestEmployeeTransfer(unittest.TestCase):
def setUp(self):
make_employee("employee2@transfers.com")
diff --git a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
index 1a665dc..f67fd7c 100644
--- a/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
+++ b/erpnext/hr/doctype/employee_transfer_property/employee_transfer_property.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeTransferProperty(Document):
pass
diff --git a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.js b/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.js
deleted file mode 100644
index 00a334a..0000000
--- a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Transfer Property", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Transfer Property
- () => frappe.tests.make('Employee Transfer Property', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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
index 39c20a6..287dac6 100644
--- a/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.py
+++ b/erpnext/hr/doctype/employee_transfer_property/test_employee_transfer_property.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeTransferProperty(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/employment_type/employment_type.py b/erpnext/hr/doctype/employment_type/employment_type.py
index fb306b6..e2a5557 100644
--- a/erpnext/hr/doctype/employment_type/employment_type.py
+++ b/erpnext/hr/doctype/employment_type/employment_type.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class EmploymentType(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.js b/erpnext/hr/doctype/employment_type/test_employment_type.js
index 9835aab..fd7c6a1 100644
--- a/erpnext/hr/doctype/employment_type/test_employment_type.js
+++ b/erpnext/hr/doctype/employment_type/test_employment_type.js
@@ -19,4 +19,4 @@
'name of employment type correctly saved'),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.py b/erpnext/hr/doctype/employment_type/test_employment_type.py
index e138136..2ba4e8c 100644
--- a/erpnext/hr/doctype/employment_type/test_employment_type.py
+++ b/erpnext/hr/doctype/employment_type/test_employment_type.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Employment Type')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Employment Type')
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/expected_skill_set/__init__.py
similarity index 100%
rename from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
rename to erpnext/hr/doctype/expected_skill_set/__init__.py
diff --git a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json
new file mode 100644
index 0000000..899f5bd
--- /dev/null
+++ b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json
@@ -0,0 +1,40 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 13:05:06.741330",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "skill",
+ "description"
+ ],
+ "fields": [
+ {
+ "fieldname": "skill",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Skill",
+ "options": "Skill",
+ "reqd": 1
+ },
+ {
+ "fetch_from": "skill.description",
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Description"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-12 14:26:33.062549",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Expected Skill Set",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py
new file mode 100644
index 0000000..27120c1
--- /dev/null
+++ b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class ExpectedSkillSet(Document):
+ pass
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 629341f..218e97d 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -10,6 +10,26 @@
},
company: function(frm) {
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+ var expenses = frm.doc.expenses;
+ for (var i = 0; i < expenses.length; i++) {
+ var expense = expenses[i];
+ if (!expense.expense_type) {
+ continue;
+ }
+ frappe.call({
+ method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center",
+ args: {
+ "expense_claim_type": expense.expense_type,
+ "company": frm.doc.company
+ },
+ callback: function(r) {
+ if (r.message) {
+ expense.default_account = r.message.account;
+ expense.cost_center = r.message.cost_center;
+ }
+ }
+ });
+ }
},
});
@@ -442,4 +462,4 @@
tax_amount: function(frm, cdt, cdn) {
frm.trigger("calculate_total_tax", cdt, cdn);
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index 95e2806..d785db7 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -2,17 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import get_fullname, flt, cstr, get_link_to_form
-from frappe.model.document import Document
-from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee
-from erpnext.accounts.party import get_party_account
-from erpnext.accounts.general_ledger import make_gl_entries
+from frappe.utils import cstr, flt, get_link_to_form
+
+import erpnext
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
+from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.controllers.accounts_controller import AccountsController
-from frappe.utils.csvutils import getlink
-from erpnext.accounts.utils import get_account_currency
+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
@@ -77,7 +77,7 @@
self.make_gl_entries()
if self.is_paid:
- update_reimbursed_amount(self)
+ update_reimbursed_amount(self, self.grand_total)
self.set_status(update=True)
self.update_claimed_amount_in_employee_advance()
@@ -89,7 +89,7 @@
self.make_gl_entries(cancel=True)
if self.is_paid:
- update_reimbursed_amount(self)
+ update_reimbursed_amount(self, -1 * self.grand_total)
self.update_claimed_amount_in_employee_advance()
@@ -270,20 +270,10 @@
if not expense.default_account or not validate:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
-def update_reimbursed_amount(doc, jv=None):
+def update_reimbursed_amount(doc, amount):
- condition = ""
-
- if jv:
- condition += "and voucher_no = '{0}'".format(jv)
-
- amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) - ifnull(sum(credit_in_account_currency), 0)as amt
- from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
- and party = %s {condition}""".format(condition=condition), #nosec
- (doc.name, doc.employee) ,as_dict=1)[0].amt
-
- doc.total_amount_reimbursed = amt
- frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", amt)
+ doc.total_amount_reimbursed += amount
+ 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)
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
index 7de8f4f..a5682dc 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'reference_name',
@@ -17,4 +19,4 @@
'items': ['Employee Advance']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.js b/erpnext/hr/doctype/expense_claim/test_expense_claim.js
index d0c43d3..2529fae 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.js
@@ -42,4 +42,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 96ea686..941fd58 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -2,12 +2,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import random_string, nowdate
-from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
+
+import frappe
+from frappe.utils import flt, nowdate, random_string
+
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
test_records = frappe.get_test_records('Expense Claim')
test_dependencies = ['Employee']
@@ -72,7 +74,7 @@
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 - _TC4",
+ expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4",
do_not_submit=True, taxes=taxes)
expense_claim.submit()
@@ -138,6 +140,31 @@
expense_claim.submit()
frappe.set_user("Administrator")
+ 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.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)
+ 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)
+ 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)
+ 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')
@@ -149,7 +176,7 @@
account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
return {'taxes':[{
"account_head": account,
- "rate": 0,
+ "rate": 9,
"description": "CGST",
"tax_amount": 10,
"total": 210
@@ -191,3 +218,22 @@
return expense_claim
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 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.reference_no = "1"
+ pe.reference_date = nowdate()
+ pe.source_exchange_rate = 1
+ pe.paid_to = payable_account
+ pe.references[0].allocated_amount = amt
+ pe.insert()
+ pe.submit()
diff --git a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py b/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py
index f34633c..a982002 100644
--- a/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py
+++ b/erpnext/hr/doctype/expense_claim_account/expense_claim_account.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ExpenseClaimAccount(Document):
pass
diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py
index c4e7b02..5607f41 100644
--- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py
+++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ExpenseClaimAdvance(Document):
pass
diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py
index 8bfa1ad..019e9f4 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ExpenseClaimDetail(Document):
- pass
\ No newline at end of file
+ pass
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 2595506..101461c 100644
--- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
+++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class ExpenseClaimType(Document):
def validate(self):
self.validate_accounts()
@@ -25,4 +27,4 @@
"""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))
\ No newline at end of file
+ ).format(entry.default_account, entry.company))
diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js
index 62234e0..3c9ed35 100644
--- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js
+++ b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js
@@ -27,4 +27,3 @@
() => done()
]);
});
-
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 1d89430..f0c900e 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
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Expense Claim Type')
diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
index 020457d..4a1064b 100644
--- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
+++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
@@ -56,8 +56,6 @@
},
{
"columns": 2,
- "fetch_from": "account_head.tax_rate",
- "fetch_if_empty": 1,
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
@@ -111,4 +109,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py
index 4103bef..596e8c7 100644
--- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py
+++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ExpenseTaxesandCharges(Document):
pass
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/full_and_final_asset/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/full_and_final_asset/__init__.py
diff --git a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.js b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.js
new file mode 100644
index 0000000..1965b46
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Full and Final Asset', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.json b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.json
new file mode 100644
index 0000000..3ad8335
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.json
@@ -0,0 +1,64 @@
+{
+ "actions": [],
+ "creation": "2021-06-28 13:36:58.658985",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "reference",
+ "asset_name",
+ "date",
+ "status",
+ "description"
+ ],
+ "fields": [
+ {
+ "fieldname": "reference",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Reference",
+ "options": "Asset Movement",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Owned\nReturned",
+ "reqd": 1
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Description"
+ },
+ {
+ "fieldname": "asset_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Asset Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "date",
+ "fieldtype": "Datetime",
+ "in_list_view": 1,
+ "label": "Date",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-07-15 15:17:31.309834",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Full and Final Asset",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.py b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.py
new file mode 100644
index 0000000..661af7d
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_asset/full_and_final_asset.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class FullandFinalAsset(Document):
+ pass
diff --git a/erpnext/hr/doctype/full_and_final_asset/test_full_and_final_asset.py b/erpnext/hr/doctype/full_and_final_asset/test_full_and_final_asset.py
new file mode 100644
index 0000000..9afe0f2
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_asset/test_full_and_final_asset.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestFullandFinalAsset(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/full_and_final_outstanding_statement/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/full_and_final_outstanding_statement/__init__.py
diff --git a/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json b/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json
new file mode 100644
index 0000000..be242e2
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.json
@@ -0,0 +1,96 @@
+{
+ "actions": [],
+ "creation": "2021-06-28 13:32:02.167317",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "component",
+ "reference_document_type",
+ "reference_document",
+ "account",
+ "paid_via_salary_slip",
+ "column_break_4",
+ "amount",
+ "status",
+ "remark"
+ ],
+ "fields": [
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "columns": 2,
+ "default": "Unsettled",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Settled\nUnsettled"
+ },
+ {
+ "fieldname": "remark",
+ "fieldtype": "Small Text",
+ "label": "Remark"
+ },
+ {
+ "columns": 2,
+ "depends_on": "reference_document_type",
+ "fieldname": "reference_document",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Reference Document",
+ "mandatory_depends_on": "reference_document_type",
+ "options": "reference_document_type",
+ "search_index": 1
+ },
+ {
+ "columns": 2,
+ "fieldname": "component",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Component",
+ "reqd": 1
+ },
+ {
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "label": "Account",
+ "options": "Account"
+ },
+ {
+ "columns": 2,
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount"
+ },
+ {
+ "columns": 2,
+ "fieldname": "reference_document_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Reference Document Type",
+ "options": "DocType"
+ },
+ {
+ "default": "0",
+ "fieldname": "paid_via_salary_slip",
+ "fieldtype": "Check",
+ "label": "Paid via Salary Slip"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-07-20 16:59:34.447934",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Full and Final Outstanding Statement",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py b/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py
new file mode 100644
index 0000000..4b239ab
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_outstanding_statement/full_and_final_outstanding_statement.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class FullandFinalOutstandingStatement(Document):
+ pass
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/full_and_final_statement/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/full_and_final_statement/__init__.py
diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.js b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.js
new file mode 100644
index 0000000..074d85b
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.js
@@ -0,0 +1,115 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Full and Final Statement', {
+ refresh: function(frm) {
+ frm.events.set_queries(frm, "payables");
+ frm.events.set_queries(frm, "receivables");
+
+ if (frm.doc.docstatus == 1 && frm.doc.status == "Unpaid") {
+ frm.add_custom_button(__("Create Journal Entry"), function () {
+ frm.events.create_journal_entry(frm);
+ });
+ }
+ },
+
+ set_queries: function(frm, type) {
+ frm.set_query("reference_document_type", type, function () {
+ let modules = ["HR", "Payroll", "Loan Management"];
+ return {
+ filters: {
+ istable: 0,
+ issingle: 0,
+ module: ["In", modules]
+ }
+ };
+ });
+
+ let filters = {};
+
+ frm.set_query('reference_document', type, function(doc, cdt, cdn) {
+ let fnf_doc = frappe.get_doc(cdt, cdn);
+
+ frappe.model.with_doctype(fnf_doc.reference_document_type, function() {
+ if (frappe.model.is_tree(fnf_doc.reference_document_type)) {
+ filters['is_group'] = 0;
+ }
+
+ if (frappe.meta.has_field(fnf_doc.reference_document_type, 'company')) {
+ filters['company'] = frm.doc.company;
+ }
+
+ if (frappe.meta.has_field(fnf_doc.reference_document_type, 'employee')) {
+ filters['employee'] = frm.doc.employee;
+ }
+ });
+
+ return {
+ filters: filters
+ };
+ });
+ },
+
+ employee: function(frm) {
+ frm.events.get_outstanding_statements(frm);
+ },
+
+ get_outstanding_statements: function(frm) {
+ if (frm.doc.employee) {
+ frappe.call({
+ method: "get_outstanding_statements",
+ doc: frm.doc,
+ callback: function() {
+ frm.refresh();
+ }
+ });
+ }
+ },
+
+ create_journal_entry: function(frm) {
+ frappe.call({
+ method: "create_journal_entry",
+ doc: frm.doc,
+ callback: function(r) {
+ var doclist = frappe.model.sync(r.message);
+ frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+ }
+ });
+ }
+});
+
+frappe.ui.form.on("Full and Final Outstanding Statement", {
+ reference_document: function(frm, cdt, cdn) {
+ var child = locals[cdt][cdn];
+ if (child.reference_document_type && child.reference_document) {
+ frappe.call({
+ method: "erpnext.hr.doctype.full_and_final_statement.full_and_final_statement.get_account_and_amount",
+ args: {
+ ref_doctype: child.reference_document_type,
+ ref_document: child.reference_document
+ },
+ callback: function(r) {
+ if (r.message) {
+ frappe.model.set_value(cdt, cdn, "account", r.message[0]);
+ frappe.model.set_value(cdt, cdn, "amount", r.message[1]);
+ }
+ }
+ });
+ }
+ },
+
+ amount: function(frm) {
+ var total_payable_amount = 0;
+ var total_receivable_amount = 0;
+
+ frm.doc.payables.forEach(element => {
+ total_payable_amount = total_payable_amount + element.amount;
+ });
+
+ frm.doc.receivables.forEach(element => {
+ total_receivable_amount = total_receivable_amount + element.amount;
+ });
+ frm.set_value("total_payable_amount", flt(total_payable_amount));
+ frm.set_value("total_receivable_amount", flt(total_receivable_amount));
+ }
+});
diff --git a/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.json b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.json
new file mode 100644
index 0000000..ebcf36d
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.json
@@ -0,0 +1,231 @@
+{
+ "actions": [],
+ "autoname": "HR-FNF-.YYYY.-.#####",
+ "creation": "2021-06-28 13:17:36.050459",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "employee",
+ "employee_name",
+ "transaction_date",
+ "column_break_12",
+ "company",
+ "status",
+ "amended_from",
+ "employee_details_section",
+ "date_of_joining",
+ "relieving_date",
+ "column_break_4",
+ "designation",
+ "department",
+ "section_break_8",
+ "payables",
+ "section_break_10",
+ "receivables",
+ "totals_section",
+ "total_payable_amount",
+ "column_break_21",
+ "total_receivable_amount",
+ "section_break_15",
+ "assets_allocated"
+ ],
+ "fields": [
+ {
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Employee",
+ "options": "Employee",
+ "reqd": 1
+ },
+ {
+ "fetch_from": "employee.employee_name",
+ "fieldname": "employee_name",
+ "fieldtype": "Data",
+ "label": "Employee Name",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "employee.designation",
+ "fieldname": "designation",
+ "fieldtype": "Link",
+ "label": "Designation",
+ "options": "Designation",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "Unpaid",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "label": "Status",
+ "options": "Paid\nUnpaid",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "employee.department",
+ "fieldname": "department",
+ "fieldtype": "Link",
+ "label": "Department",
+ "options": "Department",
+ "read_only": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Full and Final Statement",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "label": "Payables"
+ },
+ {
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break",
+ "label": "Receivables"
+ },
+ {
+ "fieldname": "assets_allocated",
+ "fieldtype": "Table",
+ "options": "Full and Final Asset"
+ },
+ {
+ "fetch_from": "employee.relieving_date",
+ "fieldname": "relieving_date",
+ "fieldtype": "Date",
+ "label": "Relieving Date ",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fetch_from": "employee.date_of_joining",
+ "fieldname": "date_of_joining",
+ "fieldtype": "Date",
+ "label": "Date of Joining",
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_15",
+ "fieldtype": "Section Break",
+ "label": "Assets Allocated"
+ },
+ {
+ "fetch_from": "employee.company",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_12",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "payables",
+ "fieldtype": "Table",
+ "options": "Full and Final Outstanding Statement"
+ },
+ {
+ "fieldname": "receivables",
+ "fieldtype": "Table",
+ "options": "Full and Final Outstanding Statement"
+ },
+ {
+ "fieldname": "employee_details_section",
+ "fieldtype": "Section Break",
+ "label": "Employee Details"
+ },
+ {
+ "fieldname": "transaction_date",
+ "fieldtype": "Date",
+ "in_standard_filter": 1,
+ "label": "Transaction Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "totals_section",
+ "fieldtype": "Section Break",
+ "label": "Totals"
+ },
+ {
+ "fieldname": "total_payable_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Total Payable Amount",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_21",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "total_receivable_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Total Receivable Amount",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-08-30 21:11:09.892560",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Full and Final Statement",
+ "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": "HR User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_changes": 1
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..f539537
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement.py
@@ -0,0 +1,177 @@
+# Copyright (c) 2021, 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, get_link_to_form, today
+
+
+class FullandFinalStatement(Document):
+ def validate(self):
+ self.get_outstanding_statements()
+ if self.docstatus == 1:
+ self.validate_settlement("payables")
+ self.validate_settlement("receivables")
+ self.validate_asset()
+
+ def validate_settlement(self, component_type):
+ for data in self.get(component_type, []):
+ if data.status == "Unsettled":
+ frappe.throw(_("Settle all Payables and Receivables before submission"))
+
+ def validate_asset(self):
+ for data in self.assets_allocated:
+ if data.status == "Owned":
+ frappe.throw(_("All allocated assets should be returned before submission"))
+
+ @frappe.whitelist()
+ def get_outstanding_statements(self):
+ if self.relieving_date:
+ if not len(self.get("payables", [])):
+ components = self.get_payable_component()
+ self.create_component_row(components, "payables")
+ if not len(self.get("receivables", [])):
+ components = self.get_receivable_component()
+ self.create_component_row(components, "receivables")
+
+ if not len(self.get("assets_allocated", [])):
+ 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)))
+
+ 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
+ })
+
+
+ def get_payable_component(self):
+ return [
+ "Salary Slip",
+ "Gratuity",
+ "Expense Claim",
+ "Bonus",
+ "Leave Encashment",
+ ]
+
+ def get_receivable_component(self):
+ return [
+ "Loan",
+ "Employee Advance",
+ ]
+
+ 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
+ }
+ )
+
+ data = []
+ inward_movements = []
+ outward_movements = []
+ for movement in asset_movements:
+ if movement.to_employee and movement.to_employee == self.employee:
+ inward_movements.append(movement)
+
+ if movement.from_employee and movement.from_employee == self.employee:
+ outward_movements.append(movement)
+
+ for movement in inward_movements:
+ outwards_count = [movement.asset for movement in outward_movements].count(movement.asset)
+ 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"
+ })
+ return data
+
+ @frappe.whitelist()
+ def create_journal_entry(self):
+ precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
+ jv = frappe.new_doc("Journal Entry")
+ jv.company = self.company
+ jv.voucher_type = "Bank Entry"
+ jv.posting_date = today()
+
+ difference = self.total_payable_amount - self.total_receivable_amount
+
+ for data in self.payables:
+ if data.amount > 0 and not data.paid_via_salary_slip:
+ account_dict = {
+ "account": data.account,
+ "debit_in_account_currency": flt(data.amount, precision)
+ }
+ if data.reference_document_type == "Expense Claim":
+ account_dict["party_type"] = "Employee"
+ account_dict["party"] = self.employee
+
+ jv.append("accounts", account_dict)
+
+ for data in self.receivables:
+ if data.amount > 0:
+ account_dict = {
+ "account": data.account,
+ "credit_in_account_currency": flt(data.amount, precision)
+ }
+ if data.reference_document_type == "Employee Advance":
+ account_dict["party_type"] = "Employee"
+ account_dict["party"] = self.employee
+
+ 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
+ })
+ 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)
+ 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
+ return [payable_account, amount]
+
+ if ref_doctype == "Gratuity":
+ 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)
+ 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)
+ 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)
+ 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/full_and_final_statement_list.js b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement_list.js
new file mode 100644
index 0000000..4aedec7
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_statement/full_and_final_statement_list.js
@@ -0,0 +1,11 @@
+frappe.listview_settings["Full and Final Statement"] = {
+ get_indicator: function(doc) {
+ var colors = {
+ "Draft": "red",
+ "Unpaid": "orange",
+ "Paid": "green",
+ "Cancelled": "red"
+ };
+ return [__(doc.status), colors[doc.status], "status,=," + doc.status];
+ }
+};
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
new file mode 100644
index 0000000..f6c1d15
--- /dev/null
+++ b/erpnext/hr/doctype/full_and_final_statement/test_full_and_final_statement.py
@@ -0,0 +1,74 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import unittest
+
+import frappe
+from frappe.utils import add_days, today
+
+from erpnext.assets.doctype.asset.test_asset import create_asset_data
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+
+class TestFullandFinalStatement(unittest.TestCase):
+
+ def setUp(self):
+ create_asset_data()
+
+ def tearDown(self):
+ frappe.db.sql("Delete from `tabFull and Final Statement`")
+ frappe.db.sql("Delete from `tabAsset`")
+ frappe.db.sql("Delete from `tabAsset Movement`")
+
+ def test_check_bootstraped_data_asset_movement_and_jv_creation(self):
+ employee = make_employee("test_fnf@example.com", company="_Test Company")
+ movement = create_asset_movement(employee)
+ 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"]
+
+ receivable_bootstraped_component = ["Loan", "Employee Advance"]
+
+ #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)
+
+ #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
+ fnf.transaction_date = today()
+ fnf.save()
+ return fnf
+
+def create_asset_movement(employee):
+ asset_name = create_asset()
+ movement = frappe.new_doc("Asset Movement")
+ movement.company = "_Test Company"
+ movement.purpose = "Issue"
+ movement.transaction_date = today()
+
+ 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")
+
+ asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
+ asset = frappe.get_doc("Asset", asset_name)
+ asset.calculate_depreciation = 0
+ asset.available_for_use_date = today()
+ asset.submit()
+ return asset_name
diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.py b/erpnext/hr/doctype/grievance_type/grievance_type.py
index 618cf0a..5d8d41c 100644
--- a/erpnext/hr/doctype/grievance_type/grievance_type.py
+++ b/erpnext/hr/doctype/grievance_type/grievance_type.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class GrievanceType(Document):
pass
diff --git a/erpnext/hr/doctype/grievance_type/test_grievance_type.py b/erpnext/hr/doctype/grievance_type/test_grievance_type.py
index a02a34d..481f4e5 100644
--- a/erpnext/hr/doctype/grievance_type/test_grievance_type.py
+++ b/erpnext/hr/doctype/grievance_type/test_grievance_type.py
@@ -4,5 +4,6 @@
# import frappe
import unittest
+
class TestGrievanceType(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/holiday/holiday.py b/erpnext/hr/doctype/holiday/holiday.py
index aabab0b..fbfe756 100644
--- a/erpnext/hr/doctype/holiday/holiday.py
+++ b/erpnext/hr/doctype/holiday/holiday.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class Holiday(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.js b/erpnext/hr/doctype/holiday_list/holiday_list.js
index 462bd8b..ea033c7 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.js
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.js
@@ -1,10 +1,10 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-frappe.ui.form.on('Holiday List', {
+frappe.ui.form.on("Holiday List", {
refresh: function(frm) {
if (frm.doc.holidays) {
- frm.set_value('total_holidays', frm.doc.holidays.length);
+ frm.set_value("total_holidays", frm.doc.holidays.length);
}
},
from_date: function(frm) {
@@ -14,3 +14,36 @@
}
}
});
+
+frappe.tour["Holiday List"] = [
+ {
+ fieldname: "holiday_list_name",
+ title: "Holiday List Name",
+ description: __("Enter a name for this Holiday List."),
+ },
+ {
+ fieldname: "from_date",
+ title: "From Date",
+ description: __("Based on your HR Policy, select your leave allocation period's start date"),
+ },
+ {
+ fieldname: "to_date",
+ title: "To Date",
+ description: __("Based on your HR Policy, select your leave allocation period's end date"),
+ },
+ {
+ fieldname: "weekly_off",
+ title: "Weekly Off",
+ description: __("Select your weekly off day"),
+ },
+ {
+ fieldname: "get_weekly_off_dates",
+ title: "Add Holidays",
+ description: __("Click on Add to Holidays. This will populate the holidays table with all the dates that fall on the selected weekly off. Repeat the process for populating the dates for all your weekly holidays"),
+ },
+ {
+ fieldname: "holidays",
+ title: "Holidays",
+ description: __("Here, your weekly offs are pre-populated based on the previous selections. You can add more rows to also add public and national holidays individually.")
+ },
+];
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py
index f65e6e1..7d1b991 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.py
@@ -1,13 +1,15 @@
-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from frappe.utils import cint, getdate, formatdate, today
-from frappe import throw, _
+
+import frappe
+from frappe import _, throw
from frappe.model.document import Document
+from frappe.utils import cint, formatdate, getdate, today
+
class OverlapError(frappe.ValidationError): pass
@@ -44,9 +46,10 @@
def get_weekly_off_date_list(self, start_date, end_date):
start_date, end_date = getdate(start_date), getdate(end_date)
- from dateutil import relativedelta
- from datetime import timedelta
import calendar
+ from datetime import timedelta
+
+ from dateutil import relativedelta
date_list = []
existing_date_list = []
@@ -90,9 +93,11 @@
update={"allDay": 1})
-def is_holiday(holiday_list, date=today()):
+def is_holiday(holiday_list, date=None):
"""Returns true if the given date is a holiday in the given holiday list
"""
+ if date is None:
+ date = today()
if holiday_list:
return bool(frappe.get_all('Holiday List',
dict(name=holiday_list, holiday_date=date)))
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
index 22e1de0..bbba36a 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'holiday_list',
@@ -18,4 +19,4 @@
'items': ['Service Level', 'Service Level Agreement']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.js b/erpnext/hr/doctype/holiday_list/test_holiday_list.js
index bfcafa9..ce76614 100644
--- a/erpnext/hr/doctype/holiday_list/test_holiday_list.js
+++ b/erpnext/hr/doctype/holiday_list/test_holiday_list.js
@@ -39,4 +39,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.py b/erpnext/hr/doctype/holiday_list/test_holiday_list.py
index 64bed66..2713193 100644
--- a/erpnext/hr/doctype/holiday_list/test_holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/test_holiday_list.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import getdate
from datetime import timedelta
+import frappe
+from frappe.utils import getdate
+
class TestHolidayList(unittest.TestCase):
def test_holiday_list(self):
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js
index fd082fd..6e26a1f 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.js
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.js
@@ -2,7 +2,22 @@
// For license information, please see license.txt
frappe.ui.form.on('HR Settings', {
- restrict_backdated_leave_application: function(frm) {
- frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application);
+});
+
+frappe.tour['HR Settings'] = [
+ {
+ fieldname: 'emp_created_by',
+ title: 'Employee Naming By',
+ description: __('Employee can be named by Employee ID if you assign one, or via Naming Series. Select your preference here.'),
+ },
+ {
+ fieldname: 'standard_working_hours',
+ title: 'Standard Working Hours',
+ description: __('Enter the Standard Working Hours for a normal work day. These hours will be used in calculations of reports such as Employee Hours Utilization and Project Profitability analysis.'),
+ },
+ {
+ fieldname: 'leave_and_expense_claim_settings',
+ title: 'Leave and Expense Clain Settings',
+ description: __('Review various other settings related to Employee Leaves and Expense Claim')
}
-});
\ No newline at end of file
+];
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 2396a8e..5148435 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -7,24 +7,36 @@
"engine": "InnoDB",
"field_order": [
"employee_settings",
- "retirement_age",
"emp_created_by",
- "column_break_4",
"standard_working_hours",
- "stop_birthday_reminders",
- "expense_approver_mandatory_in_expense_claim",
- "leave_settings",
+ "column_break_9",
+ "retirement_age",
+ "reminders_section",
+ "send_birthday_reminders",
+ "column_break_11",
+ "send_work_anniversary_reminders",
+ "column_break_18",
+ "send_holiday_reminders",
+ "frequency",
+ "leave_and_expense_claim_settings",
"send_leave_notification",
"leave_approval_notification_template",
"leave_status_notification_template",
- "role_allowed_to_create_backdated_leave_application",
- "column_break_18",
"leave_approver_mandatory_in_leave_application",
+ "restrict_backdated_leave_application",
+ "role_allowed_to_create_backdated_leave_application",
+ "column_break_29",
+ "expense_approver_mandatory_in_expense_claim",
"show_leaves_of_all_department_members_in_calendar",
"auto_leave_encashment",
- "restrict_backdated_leave_application",
- "hiring_settings",
- "check_vacancies"
+ "hiring_settings_section",
+ "check_vacancies",
+ "send_interview_reminder",
+ "interview_reminder_template",
+ "remind_before",
+ "column_break_4",
+ "send_interview_feedback_reminder",
+ "feedback_reminder_notification_template"
],
"fields": [
{
@@ -33,17 +45,16 @@
"label": "Employee Settings"
},
{
- "description": "Enter retirement age in years",
"fieldname": "retirement_age",
"fieldtype": "Data",
- "label": "Retirement Age"
+ "label": "Retirement Age (In Years)"
},
{
"default": "Naming Series",
- "description": "Employee records are created using the selected field",
+ "description": "Employee records are created using the selected option",
"fieldname": "emp_created_by",
"fieldtype": "Select",
- "label": "Employee Records to be created by",
+ "label": "Employee Naming By",
"options": "Naming Series\nEmployee Number\nFull Name"
},
{
@@ -51,41 +62,12 @@
"fieldtype": "Column Break"
},
{
- "default": "0",
- "description": "Don't send employee birthday reminders",
- "fieldname": "stop_birthday_reminders",
- "fieldtype": "Check",
- "label": "Stop Birthday Reminders"
- },
- {
"default": "1",
"fieldname": "expense_approver_mandatory_in_expense_claim",
"fieldtype": "Check",
"label": "Expense Approver Mandatory In Expense Claim"
},
{
- "collapsible": 1,
- "fieldname": "leave_settings",
- "fieldtype": "Section Break",
- "label": "Leave Settings"
- },
- {
- "depends_on": "eval: doc.send_leave_notification == 1",
- "fieldname": "leave_approval_notification_template",
- "fieldtype": "Link",
- "label": "Leave Approval Notification Template",
- "mandatory_depends_on": "eval: doc.send_leave_notification == 1",
- "options": "Email Template"
- },
- {
- "depends_on": "eval: doc.send_leave_notification == 1",
- "fieldname": "leave_status_notification_template",
- "fieldtype": "Link",
- "label": "Leave Status Notification Template",
- "mandatory_depends_on": "eval: doc.send_leave_notification == 1",
- "options": "Email Template"
- },
- {
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
@@ -102,34 +84,17 @@
"label": "Show Leaves Of All Department Members In Calendar"
},
{
- "collapsible": 1,
- "fieldname": "hiring_settings",
- "fieldtype": "Section Break",
- "label": "Hiring Settings"
- },
- {
- "default": "0",
- "fieldname": "check_vacancies",
- "fieldtype": "Check",
- "label": "Check Vacancies On Job Offer Creation"
- },
- {
"default": "0",
"fieldname": "auto_leave_encashment",
"fieldtype": "Check",
"label": "Auto Leave Encashment"
},
{
- "default": "0",
- "fieldname": "restrict_backdated_leave_application",
- "fieldtype": "Check",
- "label": "Restrict Backdated Leave Application"
- },
- {
"depends_on": "eval:doc.restrict_backdated_leave_application == 1",
"fieldname": "role_allowed_to_create_backdated_leave_application",
"fieldtype": "Link",
"label": "Role Allowed to Create Backdated Leave Application",
+ "mandatory_depends_on": "eval:doc.restrict_backdated_leave_application == 1",
"options": "Role"
},
{
@@ -139,16 +104,135 @@
"label": "Send Leave Notification"
},
{
+ "depends_on": "eval: doc.send_leave_notification == 1",
+ "fieldname": "leave_approval_notification_template",
+ "fieldtype": "Link",
+ "label": "Leave Approval Notification Template",
+ "mandatory_depends_on": "eval: doc.send_leave_notification == 1",
+ "options": "Email Template"
+ },
+ {
+ "depends_on": "eval: doc.send_leave_notification == 1",
+ "fieldname": "leave_status_notification_template",
+ "fieldtype": "Link",
+ "label": "Leave Status Notification Template",
+ "mandatory_depends_on": "eval: doc.send_leave_notification == 1",
+ "options": "Email Template"
+ },
+ {
"fieldname": "standard_working_hours",
"fieldtype": "Int",
"label": "Standard Working Hours"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "leave_and_expense_claim_settings",
+ "fieldtype": "Section Break",
+ "label": "Leave and Expense Claim Settings"
+ },
+ {
+ "default": "00:15:00",
+ "depends_on": "send_interview_reminder",
+ "fieldname": "remind_before",
+ "fieldtype": "Time",
+ "label": "Remind Before"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "reminders_section",
+ "fieldtype": "Section Break",
+ "label": "Reminders"
+ },
+ {
+ "default": "1",
+ "fieldname": "send_holiday_reminders",
+ "fieldtype": "Check",
+ "label": "Holidays"
+ },
+ {
+ "default": "1",
+ "fieldname": "send_work_anniversary_reminders",
+ "fieldtype": "Check",
+ "label": "Work Anniversaries "
+ },
+ {
+ "default": "Weekly",
+ "depends_on": "eval:doc.send_holiday_reminders",
+ "fieldname": "frequency",
+ "fieldtype": "Select",
+ "label": "Set the frequency for holiday reminders",
+ "mandatory_depends_on": "send_holiday_reminders",
+ "options": "Weekly\nMonthly"
+ },
+ {
+ "default": "1",
+ "fieldname": "send_birthday_reminders",
+ "fieldtype": "Check",
+ "label": "Birthdays"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "send_interview_reminder",
+ "fieldtype": "Check",
+ "label": "Send Interview Reminder"
+ },
+ {
+ "default": "0",
+ "fieldname": "send_interview_feedback_reminder",
+ "fieldtype": "Check",
+ "label": "Send Interview Feedback Reminder"
+ },
+ {
+ "fieldname": "column_break_29",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "send_interview_feedback_reminder",
+ "fieldname": "feedback_reminder_notification_template",
+ "fieldtype": "Link",
+ "label": "Feedback Reminder Notification Template",
+ "mandatory_depends_on": "send_interview_feedback_reminder",
+ "options": "Email Template"
+ },
+ {
+ "depends_on": "send_interview_reminder",
+ "fieldname": "interview_reminder_template",
+ "fieldtype": "Link",
+ "label": "Interview Reminder Notification Template",
+ "mandatory_depends_on": "send_interview_reminder",
+ "options": "Email Template"
+ },
+ {
+ "default": "0",
+ "fieldname": "restrict_backdated_leave_application",
+ "fieldtype": "Check",
+ "label": "Restrict Backdated Leave Application"
+ },
+ {
+ "fieldname": "hiring_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Hiring Settings"
+ },
+ {
+ "default": "0",
+ "fieldname": "check_vacancies",
+ "fieldtype": "Check",
+ "label": "Check Vacancies On Job Offer Creation"
}
],
"icon": "fa fa-cog",
"idx": 1,
"issingle": 1,
"links": [],
- "modified": "2021-05-11 10:52:56.192773",
+ "modified": "2021-10-01 23:46:11.098236",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py
index ced98fb..c295bcb 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.py
@@ -1,18 +1,78 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
# For license information, please see license.txt
-from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe.utils import format_date
+
+# Wether to proceed with frequency change
+PROCEED_WITH_FREQUENCY_CHANGE = False
class HRSettings(Document):
def validate(self):
self.set_naming_series()
+ # Based on proceed flag
+ global PROCEED_WITH_FREQUENCY_CHANGE
+ if not PROCEED_WITH_FREQUENCY_CHANGE:
+ self.validate_frequency_change()
+ 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)
+ 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'
+ )
+
+ monthly_job = frappe.get_doc(
+ 'Scheduled Job Type',
+ 'employee_reminders.send_reminders_in_advance_monthly'
+ )
+ except frappe.DoesNotExistError:
+ return
+
+ next_weekly_trigger = weekly_job.get_next_execution()
+ next_monthly_trigger = monthly_job.get_next_execution()
+
+ if self.freq_changed_from_monthly_to_weekly():
+ if next_monthly_trigger < next_weekly_trigger:
+ self.show_freq_change_warning(next_monthly_trigger, next_weekly_trigger)
+
+ elif self.freq_changed_from_weekly_to_monthly():
+ if next_monthly_trigger > next_weekly_trigger:
+ self.show_freq_change_warning(next_weekly_trigger, next_monthly_trigger)
+
+ def freq_changed_from_weekly_to_monthly(self):
+ return self.has_value_changed("frequency") and self.frequency == "Monthly"
+
+ def freq_changed_from_monthly_to_weekly(self):
+ return self.has_value_changed("frequency") and self.frequency == "Weekly"
+
+ def show_freq_change_warning(self, from_date, to_date):
+ 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',
+ primary_action={
+ 'label': frappe._('Yes, Proceed'),
+ 'client_action': 'erpnext.proceed_save_with_reminders_frequency_change'
+ },
+ raise_exception=frappe.ValidationError
+ )
+
+@frappe.whitelist()
+def set_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/hr_settings/test_hr_settings.js b/erpnext/hr/doctype/hr_settings/test_hr_settings.js
deleted file mode 100644
index f32640b..0000000
--- a/erpnext/hr/doctype/hr_settings/test_hr_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: HR Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new HR Settings
- () => frappe.tests.make('HR Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/hr_settings/test_hr_settings.py b/erpnext/hr/doctype/hr_settings/test_hr_settings.py
index b0b07b0..69a060a 100644
--- a/erpnext/hr/doctype/hr_settings/test_hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/test_hr_settings.py
@@ -3,11 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.hr.doctype.employee.test_employee import make_employee
-from frappe.utils import now_datetime
-from datetime import timedelta
+
class TestHRSettings(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/identification_document_type/identification_document_type.py b/erpnext/hr/doctype/identification_document_type/identification_document_type.py
index d9d81d2..862cd37 100644
--- a/erpnext/hr/doctype/identification_document_type/identification_document_type.py
+++ b/erpnext/hr/doctype/identification_document_type/identification_document_type.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class IdentificationDocumentType(Document):
pass
diff --git a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.js b/erpnext/hr/doctype/identification_document_type/test_identification_document_type.js
deleted file mode 100644
index 6587909..0000000
--- a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Identification Document Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Identification Document Type
- () => frappe.tests.make('Identification Document Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py b/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py
index 1265afa..87f3024 100644
--- a/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py
+++ b/erpnext/hr/doctype/identification_document_type/test_identification_document_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestIdentificationDocumentType(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/interest/interest.py b/erpnext/hr/doctype/interest/interest.py
index 2a9c19c..1b8f49f 100644
--- a/erpnext/hr/doctype/interest/interest.py
+++ b/erpnext/hr/doctype/interest/interest.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class Interest(Document):
pass
diff --git a/erpnext/hr/doctype/interest/test_interest.py b/erpnext/hr/doctype/interest/test_interest.py
index a7fe83b..f3727be 100644
--- a/erpnext/hr/doctype/interest/test_interest.py
+++ b/erpnext/hr/doctype/interest/test_interest.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Interest')
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/interview/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/interview/__init__.py
diff --git a/erpnext/hr/doctype/interview/interview.js b/erpnext/hr/doctype/interview/interview.js
new file mode 100644
index 0000000..6341e3a
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.js
@@ -0,0 +1,237 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview', {
+ onload: function (frm) {
+ frm.events.set_job_applicant_query(frm);
+
+ frm.set_query('interviewer', 'interview_details', function () {
+ return {
+ query: 'erpnext.hr.doctype.interview.interview.get_interviewer_list'
+ };
+ });
+ },
+
+ refresh: function (frm) {
+ if (frm.doc.docstatus != 2 && !frm.doc.__islocal) {
+ if (frm.doc.status === 'Pending') {
+ frm.add_custom_button(__('Reschedule Interview'), function() {
+ frm.events.show_reschedule_dialog(frm);
+ frm.refresh();
+ });
+ }
+
+ let allowed_interviewers = [];
+ frm.doc.interview_details.forEach(values => {
+ allowed_interviewers.push(values.interviewer);
+ });
+
+ if ((allowed_interviewers.includes(frappe.session.user))) {
+ frappe.db.get_value('Interview Feedback', {'interviewer': frappe.session.user, 'interview': frm.doc.name, 'docstatus': 1}, 'name', (r) => {
+ if (Object.keys(r).length === 0) {
+ frm.add_custom_button(__('Submit Feedback'), function () {
+ frappe.call({
+ method: 'erpnext.hr.doctype.interview.interview.get_expected_skill_set',
+ args: {
+ interview_round: frm.doc.interview_round
+ },
+ callback: function (r) {
+ frm.events.show_feedback_dialog(frm, r.message);
+ frm.refresh();
+ }
+ });
+ }).addClass('btn-primary');
+ }
+ });
+ }
+ }
+ },
+
+ show_reschedule_dialog: function (frm) {
+ let d = new frappe.ui.Dialog({
+ title: 'Reschedule Interview',
+ fields: [
+ {
+ label: 'Schedule On',
+ fieldname: 'scheduled_on',
+ fieldtype: 'Date',
+ reqd: 1
+ },
+ {
+ label: 'From Time',
+ fieldname: 'from_time',
+ fieldtype: 'Time',
+ reqd: 1
+ },
+ {
+ label: 'To Time',
+ fieldname: 'to_time',
+ fieldtype: 'Time',
+ reqd: 1
+ }
+ ],
+ primary_action_label: 'Reschedule',
+ primary_action(values) {
+ frm.call({
+ method: 'reschedule_interview',
+ doc: frm.doc,
+ args: {
+ scheduled_on: values.scheduled_on,
+ from_time: values.from_time,
+ to_time: values.to_time
+ }
+ }).then(() => {
+ frm.refresh();
+ d.hide();
+ });
+ }
+ });
+ d.show();
+ },
+
+ show_feedback_dialog: function (frm, data) {
+ let fields = frm.events.get_fields_for_feedback();
+
+ let d = new frappe.ui.Dialog({
+ title: __('Submit Feedback'),
+ fields: [
+ {
+ fieldname: 'skill_set',
+ fieldtype: 'Table',
+ label: __('Skill Assessment'),
+ cannot_add_rows: false,
+ in_editable_grid: true,
+ reqd: 1,
+ fields: fields,
+ data: data
+ },
+ {
+ fieldname: 'result',
+ fieldtype: 'Select',
+ options: ['', 'Cleared', 'Rejected'],
+ label: __('Result')
+ },
+ {
+ fieldname: 'feedback',
+ fieldtype: 'Small Text',
+ label: __('Feedback')
+ }
+ ],
+ size: 'large',
+ minimizable: true,
+ primary_action: function(values) {
+ frappe.call({
+ method: 'erpnext.hr.doctype.interview.interview.create_interview_feedback',
+ args: {
+ data: values,
+ interview_name: frm.doc.name,
+ interviewer: frappe.session.user,
+ job_applicant: frm.doc.job_applicant
+ }
+ }).then(() => {
+ frm.refresh();
+ });
+ d.hide();
+ }
+ });
+ d.show();
+ },
+
+ get_fields_for_feedback: function () {
+ return [{
+ fieldtype: 'Link',
+ fieldname: 'skill',
+ options: 'Skill',
+ in_list_view: 1,
+ label: __('Skill')
+ }, {
+ fieldtype: 'Rating',
+ fieldname: 'rating',
+ label: __('Rating'),
+ in_list_view: 1,
+ reqd: 1,
+ }];
+ },
+
+ set_job_applicant_query: function (frm) {
+ frm.set_query('job_applicant', function () {
+ let job_applicant_filters = {
+ status: ['!=', 'Rejected']
+ };
+ if (frm.doc.designation) {
+ job_applicant_filters.designation = frm.doc.designation;
+ }
+ return {
+ filters: job_applicant_filters
+ };
+ });
+ },
+
+ interview_round: async function (frm) {
+ frm.events.reset_values(frm);
+ frm.set_value('job_applicant', '');
+
+ let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message;
+ frm.set_value('designation', round_data.designation);
+ frm.events.set_job_applicant_query(frm);
+
+ if (frm.doc.interview_round) {
+ frm.events.set_interview_details(frm);
+ } else {
+ frm.set_value('interview_details', []);
+ }
+ },
+
+ set_interview_details: function (frm) {
+ frappe.call({
+ method: 'erpnext.hr.doctype.interview.interview.get_interviewers',
+ args: {
+ interview_round: frm.doc.interview_round
+ },
+ callback: function (data) {
+ let interview_details = data.message;
+ frm.set_value('interview_details', []);
+ if (data.message.length) {
+ frm.set_value('interview_details', interview_details);
+ }
+ }
+ });
+ },
+
+ job_applicant: function (frm) {
+ if (!frm.doc.interview_round) {
+ frm.doc.job_applicant = '';
+ frm.refresh();
+ frappe.throw(__('Select Interview Round First'));
+ }
+
+ if (frm.doc.job_applicant) {
+ frm.events.set_designation_and_job_opening(frm);
+ } else {
+ frm.events.reset_values(frm);
+ }
+ },
+
+ set_designation_and_job_opening: async function (frm) {
+ let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message;
+ frm.set_value('designation', round_data.designation);
+ frm.events.set_job_applicant_query(frm);
+
+ let job_applicant_data = (await frappe.db.get_value(
+ 'Job Applicant', frm.doc.job_applicant, ['designation', 'job_title', 'resume_link'],
+ )).message;
+
+ if (!round_data.designation) {
+ frm.set_value('designation', job_applicant_data.designation);
+ }
+
+ frm.set_value('job_opening', job_applicant_data.job_title);
+ frm.set_value('resume_link', job_applicant_data.resume_link);
+ },
+
+ reset_values: function (frm) {
+ frm.set_value('designation', '');
+ frm.set_value('job_opening', '');
+ frm.set_value('resume_link', '');
+ }
+});
diff --git a/erpnext/hr/doctype/interview/interview.json b/erpnext/hr/doctype/interview/interview.json
new file mode 100644
index 0000000..0d393e7
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.json
@@ -0,0 +1,254 @@
+{
+ "actions": [],
+ "autoname": "HR-INT-.YYYY.-.####",
+ "creation": "2021-04-12 15:03:11.524090",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "interview_details_section",
+ "interview_round",
+ "job_applicant",
+ "job_opening",
+ "designation",
+ "resume_link",
+ "column_break_4",
+ "status",
+ "scheduled_on",
+ "from_time",
+ "to_time",
+ "interview_feedback_section",
+ "interview_details",
+ "ratings_section",
+ "expected_average_rating",
+ "column_break_12",
+ "average_rating",
+ "section_break_13",
+ "interview_summary",
+ "reminded",
+ "amended_from"
+ ],
+ "fields": [
+ {
+ "fieldname": "job_applicant",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Job Applicant",
+ "options": "Job Applicant",
+ "reqd": 1
+ },
+ {
+ "fieldname": "job_opening",
+ "fieldtype": "Link",
+ "label": "Job Opening",
+ "options": "Job Opening",
+ "read_only": 1
+ },
+ {
+ "fieldname": "interview_round",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Interview Round",
+ "options": "Interview Round",
+ "reqd": 1
+ },
+ {
+ "default": "Pending",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "options": "Pending\nUnder Review\nCleared\nRejected",
+ "reqd": 1
+ },
+ {
+ "fieldname": "ratings_section",
+ "fieldtype": "Section Break",
+ "label": "Ratings"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "average_rating",
+ "fieldtype": "Rating",
+ "in_list_view": 1,
+ "label": "Obtained Average Rating",
+ "read_only": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "interview_summary",
+ "fieldtype": "Text"
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "resume_link",
+ "fieldtype": "Data",
+ "label": "Resume link"
+ },
+ {
+ "fieldname": "interview_details_section",
+ "fieldtype": "Section Break",
+ "label": "Details"
+ },
+ {
+ "fetch_from": "interview_round.expected_average_rating",
+ "fieldname": "expected_average_rating",
+ "fieldtype": "Rating",
+ "label": "Expected Average Rating",
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "section_break_13",
+ "fieldtype": "Section Break",
+ "label": "Interview Summary"
+ },
+ {
+ "fieldname": "column_break_12",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "interview_round.designation",
+ "fieldname": "designation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Designation",
+ "options": "Designation",
+ "read_only": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Interview",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "scheduled_on",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Scheduled On",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "reminded",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Reminded"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "interview_details",
+ "fieldtype": "Table",
+ "options": "Interview Detail"
+ },
+ {
+ "fieldname": "interview_feedback_section",
+ "fieldtype": "Section Break",
+ "label": "Feedback"
+ },
+ {
+ "fieldname": "from_time",
+ "fieldtype": "Time",
+ "in_list_view": 1,
+ "label": "From Time",
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "to_time",
+ "fieldtype": "Time",
+ "in_list_view": 1,
+ "label": "To Time",
+ "reqd": 1,
+ "set_only_once": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [
+ {
+ "link_doctype": "Interview Feedback",
+ "link_fieldname": "interview"
+ }
+ ],
+ "modified": "2021-09-30 13:30:05.421035",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 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
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Interviewer",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "job_applicant",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview/interview.py b/erpnext/hr/doctype/interview/interview.py
new file mode 100644
index 0000000..955acca
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.py
@@ -0,0 +1,293 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import datetime
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import cstr, get_datetime, get_link_to_form
+
+
+class DuplicateInterviewRoundError(frappe.ValidationError):
+ pass
+
+class Interview(Document):
+ def validate(self):
+ self.validate_duplicate_interview()
+ self.validate_designation()
+ self.validate_overlap()
+
+ 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'))
+
+ def validate_duplicate_interview(self):
+ 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)
+ ))
+
+ def validate_designation(self):
+ 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)
+ else:
+ self.designation = applicant_designation
+
+ def validate_overlap(self):
+ interviewers = [entry.interviewer for entry in self.interview_details] or ['']
+
+ overlaps = frappe.db.sql("""
+ SELECT interview.name
+ FROM `tabInterview` as interview
+ INNER JOIN `tabInterview Detail` as detail
+ WHERE
+ interview.scheduled_on = %s and interview.name != %s and interview.docstatus != 2
+ and (interview.job_applicant = %s or detail.interviewer IN %s) and
+ ((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))
+
+ if overlaps:
+ overlapping_details = _('Interview overlaps with {0}').format(get_link_to_form('Interview', overlaps[0][0]))
+ frappe.throw(overlapping_details, title=_('Overlap'))
+
+
+ @frappe.whitelist()
+ def reschedule_interview(self, scheduled_on, from_time, to_time):
+ original_date = self.scheduled_on
+ 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.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),
+ reference_doctype=self.doctype,
+ reference_name=self.name
+ )
+ except Exception:
+ frappe.msgprint(_('Failed to send the Interview Reschedule notification. Please configure your email account.'))
+
+ frappe.msgprint(_('Interview Rescheduled successfully'), indicator='green')
+
+
+def get_recipients(name, for_feedback=0):
+ 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'))
+
+ return recipients
+
+
+@frappe.whitelist()
+def get_interviewers(interview_round):
+ 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)
+
+ 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')
+ reminder_date_time = datetime.datetime.now() + datetime.timedelta(
+ 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]
+ })
+
+ interview_template = frappe.get_doc('Email Template', reminder_settings.interview_reminder_template)
+
+ for d in interviews:
+ 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,
+ subject=interview_template.subject,
+ message=message,
+ reference_doctype=doc.doctype,
+ reference_name=doc.name
+ )
+
+ 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)
+
+ 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]})
+
+ for entry in interviews:
+ recipients = get_recipients(entry.name, for_feedback=1)
+
+ 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,
+ subject=interview_feedback_template.subject,
+ message=message,
+ 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'])
+
+
+@frappe.whitelist()
+def create_interview_feedback(data, interview_name, interviewer, job_applicant):
+ import json
+
+ from six import string_types
+
+ if isinstance(data, string_types):
+ data = frappe._dict(json.loads(data))
+
+ if frappe.session.user != interviewer:
+ frappe.throw(_('Only Interviewer Are allowed to submit 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.feedback = data.feedback
+ interview_feedback.result = data.result
+
+ interview_feedback.save()
+ interview_feedback.submit()
+
+ 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']
+ ]
+
+ 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)
+
+
+@frappe.whitelist()
+def get_events(start, end, filters=None):
+ """Returns events for Gantt / 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
+
+ events = []
+
+ event_color = {
+ "Pending": "#fff4f0",
+ "Under Review": "#d3e8fc",
+ "Cleared": "#eaf5ed",
+ "Rejected": "#fce7e7"
+ }
+
+ conditions = get_event_conditions('Interview', filters)
+
+ 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,
+ `tabInterview`.to_time as to_time
+ from
+ `tabInterview`
+ where
+ (`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})
+
+ for d in interviews:
+ subject_data = []
+ for field in ["name", "job_applicant", "interview_round"]:
+ if not d.get(field):
+ continue
+ subject_data.append(d.get(field))
+
+ 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"
+ }
+
+ events.append(interview_data)
+
+ return events
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview/interview_calendar.js b/erpnext/hr/doctype/interview/interview_calendar.js
new file mode 100644
index 0000000..b46b72e
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_calendar.js
@@ -0,0 +1,14 @@
+
+frappe.views.calendar['Interview'] = {
+ field_map: {
+ 'start': 'from',
+ 'end': 'to',
+ 'id': 'name',
+ 'title': 'subject',
+ 'allDay': 'allDay',
+ 'color': 'color'
+ },
+ order_by: 'scheduled_on',
+ gantt: true,
+ get_events_method: 'erpnext.hr.doctype.interview.interview.get_events'
+};
diff --git a/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html b/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html
new file mode 100644
index 0000000..8d39fb5
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html
@@ -0,0 +1,5 @@
+<h1>Interview Feedback Reminder</h1>
+
+<p>
+ Interview Feedback for Interview {{ name }} is not submitted yet. Please submit your feedback. Thank you, good day!
+</p>
diff --git a/erpnext/hr/doctype/interview/interview_list.js b/erpnext/hr/doctype/interview/interview_list.js
new file mode 100644
index 0000000..b1f072f
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings['Interview'] = {
+ has_indicator_for_draft: 1,
+ get_indicator: function(doc) {
+ let status_color = {
+ 'Pending': 'orange',
+ 'Under Review': 'blue',
+ 'Cleared': 'green',
+ 'Rejected': 'red',
+ };
+ return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status];
+ }
+};
diff --git a/erpnext/hr/doctype/interview/interview_reminder_notification_template.html b/erpnext/hr/doctype/interview/interview_reminder_notification_template.html
new file mode 100644
index 0000000..76de46e
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_reminder_notification_template.html
@@ -0,0 +1,5 @@
+<h1>Interview Reminder</h1>
+
+<p>
+ Interview: {{name}} is scheduled on {{scheduled_on}} from {{from_time}} to {{to_time}}
+</p>
diff --git a/erpnext/hr/doctype/interview/test_interview.py b/erpnext/hr/doctype/interview/test_interview.py
new file mode 100644
index 0000000..4612e17
--- /dev/null
+++ b/erpnext/hr/doctype/interview/test_interview.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import datetime
+import os
+import unittest
+
+import frappe
+from frappe import _
+from frappe.core.doctype.user_permission.test_user_permission import create_user
+from frappe.utils import add_days, getdate, nowtime
+
+from erpnext.hr.doctype.designation.test_designation import create_designation
+from erpnext.hr.doctype.interview.interview import DuplicateInterviewRoundError
+from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+
+
+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)
+ 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))
+
+ 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.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%")})
+ self.assertIsNotNone(notification)
+
+ def test_notification_for_scheduling(self):
+ from erpnext.hr.doctype.interview.interview import send_interview_reminder
+
+ setup_reminder_settings()
+
+ job_applicant = create_job_applicant()
+ scheduled_on = datetime.datetime.now() + datetime.timedelta(minutes=10)
+
+ interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on)
+
+ frappe.db.sql("DELETE FROM `tabEmail Queue`")
+ send_interview_reminder()
+
+ interview.reload()
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertTrue("Subject: Interview Reminder" in email_queue[0].message)
+
+ def test_notification_for_feedback_submission(self):
+ from erpnext.hr.doctype.interview.interview import send_daily_feedback_reminder
+
+ setup_reminder_settings()
+
+ job_applicant = create_job_applicant()
+ scheduled_on = add_days(getdate(), -4)
+ create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on)
+
+ frappe.db.sql("DELETE FROM `tabEmail Queue`")
+ send_daily_feedback_reminder()
+
+ email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+ self.assertTrue("Subject: Interview Feedback Reminder" in email_queue[0].message)
+
+ 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):
+ if designation:
+ 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
+ )
+
+ interview = frappe.new_doc("Interview")
+ interview.interview_round = interview_round.name
+ interview.job_applicant = job_applicant
+ interview.scheduled_on = scheduled_on or getdate()
+ interview.from_time = from_time or nowtime()
+ interview.to_time = to_time or nowtime()
+
+ interview.append("interview_details", {"interviewer": interviewer_1.name})
+ interview.append("interview_details", {"interviewer": interviewer_2.name})
+
+ if save:
+ interview.save()
+
+ 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")
+ interview_round.round_name = name
+ interview_round.interview_type = create_interview_type()
+ interview_round.expected_average_rating = 4
+ if designation:
+ interview_round.designation = designation
+
+ for skill in skill_set:
+ interview_round.append("expected_skill_set", {"skill": skill})
+
+ for interviewer in interviewers:
+ 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):
+ doc = frappe.new_doc("Skill")
+ 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
+ else:
+ doc = frappe.new_doc("Interview Type")
+ doc.name = name
+ doc.description = "_Test_Description"
+ doc.save()
+
+ 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'))
+
+ 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'))
+
+ 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.save()
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/interview_detail/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/interview_detail/__init__.py
diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.js b/erpnext/hr/doctype/interview_detail/interview_detail.js
new file mode 100644
index 0000000..88518ca
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview Detail', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.json b/erpnext/hr/doctype/interview_detail/interview_detail.json
new file mode 100644
index 0000000..b5b49c0
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.json
@@ -0,0 +1,74 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 16:24:10.382863",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "interviewer",
+ "interview_feedback",
+ "average_rating",
+ "result",
+ "column_break_4",
+ "comments"
+ ],
+ "fields": [
+ {
+ "fieldname": "interviewer",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Interviewer",
+ "options": "User"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "interview_feedback",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Interview Feedback",
+ "options": "Interview Feedback",
+ "read_only": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "average_rating",
+ "fieldtype": "Rating",
+ "in_list_view": 1,
+ "label": "Average Rating",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "allow_on_submit": 1,
+ "fetch_from": "interview_feedback.feedback",
+ "fieldname": "comments",
+ "fieldtype": "Text",
+ "label": "Comments",
+ "read_only": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "result",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Result",
+ "options": "\nCleared\nRejected",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-29 13:13:25.865063",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.py b/erpnext/hr/doctype/interview_detail/interview_detail.py
new file mode 100644
index 0000000..8be3d34
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class InterviewDetail(Document):
+ pass
diff --git a/erpnext/hr/doctype/interview_detail/test_interview_detail.py b/erpnext/hr/doctype/interview_detail/test_interview_detail.py
new file mode 100644
index 0000000..a29dfff
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/test_interview_detail.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+
+class TestInterviewDetail(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/interview_feedback/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/interview_feedback/__init__.py
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.js b/erpnext/hr/doctype/interview_feedback/interview_feedback.js
new file mode 100644
index 0000000..dec559f
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.js
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview Feedback', {
+ onload: function(frm) {
+ frm.ignore_doctypes_on_cancel_all = ['Interview'];
+
+ frm.set_query('interview', function() {
+ return {
+ filters: {
+ docstatus: ['!=', 2]
+ }
+ };
+ });
+ },
+
+ interview_round: function(frm) {
+ frappe.call({
+ method: 'erpnext.hr.doctype.interview.interview.get_expected_skill_set',
+ args: {
+ interview_round: frm.doc.interview_round
+ },
+ callback: function(r) {
+ frm.set_value('skill_assessment', r.message);
+ }
+ });
+ },
+
+ interview: function(frm) {
+ frappe.call({
+ method: 'erpnext.hr.doctype.interview_feedback.interview_feedback.get_applicable_interviewers',
+ args: {
+ interview: frm.doc.interview || ''
+ },
+ callback: function(r) {
+ frm.set_query('interviewer', function() {
+ return {
+ filters: {
+ name: ['in', r.message]
+ }
+ };
+ });
+ }
+ });
+
+ },
+
+ interviewer: function(frm) {
+ if (!frm.doc.interview) {
+ frappe.throw(__('Select Interview first'));
+ frm.set_value('interviewer', '');
+ }
+ }
+});
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.json b/erpnext/hr/doctype/interview_feedback/interview_feedback.json
new file mode 100644
index 0000000..6a2f7e8
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.json
@@ -0,0 +1,171 @@
+{
+ "actions": [],
+ "autoname": "HR-INT-FEED-.####",
+ "creation": "2021-04-12 17:03:13.833285",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "details_section",
+ "interview",
+ "interview_round",
+ "job_applicant",
+ "column_break_3",
+ "interviewer",
+ "result",
+ "section_break_4",
+ "skill_assessment",
+ "average_rating",
+ "section_break_7",
+ "feedback",
+ "amended_from"
+ ],
+ "fields": [
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "interview",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Interview",
+ "options": "Interview",
+ "reqd": 1
+ },
+ {
+ "allow_in_quick_entry": 1,
+ "fetch_from": "interview.interview_round",
+ "fieldname": "interview_round",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Interview Round",
+ "options": "Interview Round",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "interviewer",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Interviewer",
+ "options": "User",
+ "reqd": 1
+ },
+ {
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "label": "Skill Assessment"
+ },
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "skill_assessment",
+ "fieldtype": "Table",
+ "options": "Skill Assessment",
+ "reqd": 1
+ },
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "average_rating",
+ "fieldtype": "Rating",
+ "in_list_view": 1,
+ "label": "Average Rating",
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break",
+ "label": "Feedback"
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Interview Feedback",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "allow_in_quick_entry": 1,
+ "fieldname": "feedback",
+ "fieldtype": "Text"
+ },
+ {
+ "fieldname": "result",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Result",
+ "options": "\nCleared\nRejected",
+ "reqd": 1
+ },
+ {
+ "fieldname": "details_section",
+ "fieldtype": "Section Break",
+ "label": "Details"
+ },
+ {
+ "fetch_from": "interview.job_applicant",
+ "fieldname": "job_applicant",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Job Applicant",
+ "options": "Job Applicant",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-09-30 13:30:49.955352",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Feedback",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "share": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Interviewer",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "interviewer",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.py b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
new file mode 100644
index 0000000..1c5a494
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import flt, get_link_to_form, getdate
+
+
+class InterviewFeedback(Document):
+ def validate(self):
+ self.validate_interviewer()
+ self.validate_interview_date()
+ self.validate_duplicate()
+ self.calculate_average_rating()
+
+ def on_submit(self):
+ self.update_interview_details()
+
+ def on_cancel(self):
+ self.update_interview_details()
+
+ 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)))
+
+ def validate_interview_date(self):
+ 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')
+ ))
+
+ def validate_duplicate(self):
+ 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)))
+
+ def calculate_average_rating(self):
+ total_rating = 0
+ for d in self.skill_assessment:
+ if d.rating:
+ total_rating += d.rating
+
+ 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)
+ total_rating = 0
+
+ if self.docstatus == 2:
+ for entry in doc.interview_details:
+ if entry.interview_feedback == self.name:
+ entry.average_rating = entry.interview_feedback = entry.comments = entry.result = None
+ break
+ else:
+ for entry in doc.interview_details:
+ if entry.interviewer == self.interviewer:
+ entry.average_rating = self.average_rating
+ entry.interview_feedback = self.name
+ entry.comments = self.feedback
+ entry.result = self.result
+
+ if entry.average_rating:
+ total_rating += entry.average_rating
+
+ doc.average_rating = flt(total_rating / len(doc.interview_details) if len(doc.interview_details) else 0)
+ doc.save()
+ doc.notify_update()
+
+
+@frappe.whitelist()
+def get_applicable_interviewers(interview):
+ 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
new file mode 100644
index 0000000..c4b7981
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+import frappe
+from frappe.utils import add_days, flt, getdate
+
+from erpnext.hr.doctype.interview.test_interview import (
+ create_interview_and_dependencies,
+ create_skill_set,
+)
+from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+
+
+class TestInterviewFeedback(unittest.TestCase):
+ 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))
+ skill_ratings = get_skills_rating(interview.interview_round)
+
+ interviewer = interview.interview_details[0].interviewer
+ create_skill_set(['Leadership'])
+
+ interview_feedback = create_interview_feedback(interview.name, interviewer, skill_ratings)
+ interview_feedback.append("skill_assessment", {"skill": 'Leadership', 'rating': 4})
+ frappe.set_user(interviewer)
+
+ self.assertRaises(frappe.ValidationError, interview_feedback.save)
+
+ frappe.set_user("Administrator")
+
+ 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))
+ skill_ratings = get_skills_rating(interview.interview_round)
+
+ # For First Interviewer Feedback
+ interviewer = interview.interview_details[0].interviewer
+ frappe.set_user(interviewer)
+
+ # calculating Average
+ feedback_1 = create_interview_feedback(interview.name, interviewer, skill_ratings)
+
+ total_rating = 0
+ for d in feedback_1.skill_assessment:
+ 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)
+
+ self.assertEqual(flt(avg_rating, 3), feedback_1.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(avg_on_interview_detail, round(feedback_1.average_rating))
+
+ '''For Second Interviewer Feedback'''
+ interviewer = interview.interview_details[1].interviewer
+ frappe.set_user(interviewer)
+
+ feedback_2 = create_interview_feedback(interview.name, interviewer, skill_ratings)
+ interview.reload()
+
+ feedback_2.cancel()
+ interview.reload()
+
+ frappe.set_user("Administrator")
+
+ def tearDown(self):
+ frappe.db.rollback()
+
+
+def create_interview_feedback(interview, interviewer, skills_ratings):
+ interview_feedback = frappe.new_doc("Interview Feedback")
+ interview_feedback.interview = interview
+ interview_feedback.interviewer = interviewer
+ interview_feedback.result = "Cleared"
+
+ for rating in skills_ratings:
+ interview_feedback.append("skill_assessment", rating)
+
+ interview_feedback.save()
+ interview_feedback.submit()
+
+ return interview_feedback
+
+
+def get_skills_rating(interview_round):
+ import random
+
+ skills = frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields = ["skill"])
+ for d in skills:
+ d["rating"] = random.randint(1, 5)
+ return skills
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/interview_round/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/interview_round/__init__.py
diff --git a/erpnext/hr/doctype/interview_round/interview_round.js b/erpnext/hr/doctype/interview_round/interview_round.js
new file mode 100644
index 0000000..6a608b0
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.js
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Interview Round", {
+ refresh: function(frm) {
+ if (!frm.doc.__islocal) {
+ frm.add_custom_button(__("Create Interview"), function() {
+ frm.events.create_interview(frm);
+ });
+ }
+ },
+ create_interview: function(frm) {
+ frappe.call({
+ method: "erpnext.hr.doctype.interview_round.interview_round.create_interview",
+ args: {
+ doc: frm.doc
+ },
+ callback: function (r) {
+ var doclist = frappe.model.sync(r.message);
+ frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+ }
+ });
+ }
+});
diff --git a/erpnext/hr/doctype/interview_round/interview_round.json b/erpnext/hr/doctype/interview_round/interview_round.json
new file mode 100644
index 0000000..9c95185
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.json
@@ -0,0 +1,118 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:round_name",
+ "creation": "2021-04-12 12:57:19.902866",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "round_name",
+ "interview_type",
+ "interviewers",
+ "column_break_3",
+ "designation",
+ "expected_average_rating",
+ "expected_skills_section",
+ "expected_skill_set"
+ ],
+ "fields": [
+ {
+ "fieldname": "round_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Round Name",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "designation",
+ "fieldtype": "Link",
+ "label": "Designation",
+ "options": "Designation"
+ },
+ {
+ "fieldname": "expected_skills_section",
+ "fieldtype": "Section Break",
+ "label": "Expected Skillset"
+ },
+ {
+ "fieldname": "expected_skill_set",
+ "fieldtype": "Table",
+ "options": "Expected Skill Set",
+ "reqd": 1
+ },
+ {
+ "fieldname": "expected_average_rating",
+ "fieldtype": "Rating",
+ "label": "Expected Average Rating",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "interview_type",
+ "fieldtype": "Link",
+ "label": "Interview Type",
+ "options": "Interview Type",
+ "reqd": 1
+ },
+ {
+ "fieldname": "interviewers",
+ "fieldtype": "Table MultiSelect",
+ "label": "Interviewers",
+ "options": "Interviewer"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-09-30 13:01:25.666660",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Round",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Interviewer",
+ "select": 1,
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_round/interview_round.py b/erpnext/hr/doctype/interview_round/interview_round.py
new file mode 100644
index 0000000..8230c78
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import json
+
+import frappe
+from frappe.model.document import Document
+
+
+class InterviewRound(Document):
+ pass
+
+@frappe.whitelist()
+def create_interview(doc):
+ if isinstance(doc, str):
+ doc = json.loads(doc)
+ doc = frappe.get_doc(doc)
+
+ interview = frappe.new_doc("Interview")
+ interview.interview_round = doc.name
+ interview.designation = doc.designation
+
+ if doc.interviewers:
+ interview.interview_details = []
+ for data in doc.interviewers:
+ 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
new file mode 100644
index 0000000..932d3de
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/test_interview_round.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+# import frappe
+
+
+class TestInterviewRound(unittest.TestCase):
+ pass
+
diff --git a/erpnext/healthcare/doctype/__init__.py b/erpnext/hr/doctype/interview_type/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/__init__.py
rename to erpnext/hr/doctype/interview_type/__init__.py
diff --git a/erpnext/hr/doctype/interview_type/interview_type.js b/erpnext/hr/doctype/interview_type/interview_type.js
new file mode 100644
index 0000000..af77b52
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview Type', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/hr/doctype/interview_type/interview_type.json b/erpnext/hr/doctype/interview_type/interview_type.json
new file mode 100644
index 0000000..14636a1
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.json
@@ -0,0 +1,73 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2021-04-12 14:44:40.664034",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "description"
+ ],
+ "fields": [
+ {
+ "fieldname": "description",
+ "fieldtype": "Text",
+ "in_list_view": 1,
+ "label": "Description"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [
+ {
+ "link_doctype": "Interview Round",
+ "link_fieldname": "interview_type"
+ }
+ ],
+ "modified": "2021-09-30 13:00:16.471518",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Type",
+ "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": "HR Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_type/interview_type.py b/erpnext/hr/doctype/interview_type/interview_type.py
new file mode 100644
index 0000000..ee5be54
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class InterviewType(Document):
+ pass
diff --git a/erpnext/hr/doctype/interview_type/test_interview_type.py b/erpnext/hr/doctype/interview_type/test_interview_type.py
new file mode 100644
index 0000000..a5d3cf9
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/test_interview_type.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+
+class TestInterviewType(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/employee_onboarding_activity/__init__.py b/erpnext/hr/doctype/interviewer/__init__.py
similarity index 100%
copy from erpnext/hr/doctype/employee_onboarding_activity/__init__.py
copy to erpnext/hr/doctype/interviewer/__init__.py
diff --git a/erpnext/hr/doctype/interviewer/interviewer.json b/erpnext/hr/doctype/interviewer/interviewer.json
new file mode 100644
index 0000000..a37b8b0
--- /dev/null
+++ b/erpnext/hr/doctype/interviewer/interviewer.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 17:38:19.354734",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "user"
+ ],
+ "fields": [
+ {
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "User",
+ "options": "User"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-13 13:41:35.817568",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interviewer",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interviewer/interviewer.py b/erpnext/hr/doctype/interviewer/interviewer.py
new file mode 100644
index 0000000..1c8dbbe
--- /dev/null
+++ b/erpnext/hr/doctype/interviewer/interviewer.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class Interviewer(Document):
+ pass
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js
index c625155..d7b1c6c 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.js
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.js
@@ -8,6 +8,24 @@
frappe.ui.form.on("Job Applicant", {
refresh: function(frm) {
+ frm.set_query("job_title", function() {
+ return {
+ filters: {
+ 'status': 'Open'
+ }
+ };
+ });
+ frm.events.create_custom_buttons(frm);
+ frm.events.make_dashboard(frm);
+ },
+
+ create_custom_buttons: function(frm) {
+ if (!frm.doc.__islocal && frm.doc.status !== "Rejected" && frm.doc.status !== "Accepted") {
+ frm.add_custom_button(__("Create Interview"), function() {
+ frm.events.create_dialog(frm);
+ });
+ }
+
if (!frm.doc.__islocal) {
if (frm.doc.__onload && frm.doc.__onload.job_offer) {
$('[data-doctype="Employee Onboarding"]').find("button").show();
@@ -28,14 +46,57 @@
});
}
}
+ },
- frm.set_query("job_title", function() {
- return {
- filters: {
- 'status': 'Open'
- }
- };
+ make_dashboard: function(frm) {
+ frappe.call({
+ method: "erpnext.hr.doctype.job_applicant.job_applicant.get_interview_details",
+ args: {
+ job_applicant: frm.doc.name
+ },
+ callback: function(r) {
+ $("div").remove(".form-dashboard-section.custom");
+ frm.dashboard.add_section(
+ frappe.render_template('job_applicant_dashboard', {
+ data: r.message
+ }),
+ __("Interview Summary")
+ );
+ }
});
+ },
+ create_dialog: function(frm) {
+ let d = new frappe.ui.Dialog({
+ title: 'Enter Interview Round',
+ fields: [
+ {
+ label: 'Interview Round',
+ fieldname: 'interview_round',
+ fieldtype: 'Link',
+ options: 'Interview Round'
+ },
+ ],
+ primary_action_label: 'Create Interview',
+ primary_action(values) {
+ frm.events.create_interview(frm, values);
+ d.hide();
+ }
+ });
+ d.show();
+ },
+
+ create_interview: function (frm, values) {
+ frappe.call({
+ method: "erpnext.hr.doctype.job_applicant.job_applicant.create_interview",
+ args: {
+ doc: frm.doc,
+ interview_round: values.interview_round
+ },
+ callback: function (r) {
+ var doclist = frappe.model.sync(r.message);
+ frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+ }
+ });
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json
index bcea5f5..200f675 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.json
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.json
@@ -9,16 +9,20 @@
"email_append_to": 1,
"engine": "InnoDB",
"field_order": [
+ "details_section",
"applicant_name",
"email_id",
"phone_number",
"country",
- "status",
"column_break_3",
"job_title",
+ "designation",
+ "status",
+ "source_and_rating_section",
"source",
"source_name",
"employee_referral",
+ "column_break_13",
"applicant_rating",
"section_break_6",
"notes",
@@ -84,7 +88,8 @@
},
{
"fieldname": "section_break_6",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Resume"
},
{
"fieldname": "cover_letter",
@@ -160,13 +165,34 @@
"label": "Employee Referral",
"options": "Employee Referral",
"read_only": 1
+ },
+ {
+ "fieldname": "details_section",
+ "fieldtype": "Section Break",
+ "label": "Details"
+ },
+ {
+ "fieldname": "source_and_rating_section",
+ "fieldtype": "Section Break",
+ "label": "Source and Rating"
+ },
+ {
+ "fieldname": "column_break_13",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "job_opening.designation",
+ "fieldname": "designation",
+ "fieldtype": "Link",
+ "label": "Designation",
+ "options": "Designation"
}
],
"icon": "fa fa-user",
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-03-24 15:51:11.117517",
+ "modified": "2021-09-29 23:06:10.904260",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Applicant",
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py
index 0594ba3..151f492 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.py
@@ -4,10 +4,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
+
import frappe
from frappe import _
-from frappe.utils import comma_and, validate_email_address
+from frappe.model.document import Document
+from frappe.utils import validate_email_address
+
+from erpnext.hr.doctype.interview.interview import get_interviewers
+
class DuplicationError(frappe.ValidationError): pass
@@ -24,7 +28,6 @@
self.name = " - ".join(keys)
def validate(self):
- self.check_email_id_is_unique()
if self.email_id:
validate_email_address(self.email_id, True)
@@ -42,12 +45,44 @@
elif self.status in ["Accepted", "Rejected"]:
emp_ref.db_set("status", self.status)
+@frappe.whitelist()
+def create_interview(doc, interview_round):
+ import json
- def check_email_id_is_unique(self):
- if self.email_id:
- names = frappe.db.sql_list("""select name from `tabJob Applicant`
- where email_id=%s and name!=%s and job_title=%s""", (self.email_id, self.name, self.job_title))
+ from six import string_types
- if names:
- frappe.throw(_("Email Address must be unique, already exists for {0}").format(comma_and(names)), frappe.DuplicateEntryError)
+ if isinstance(doc, string_types):
+ doc = json.loads(doc)
+ doc = frappe.get_doc(doc)
+ 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))
+
+ interview = frappe.new_doc("Interview")
+ interview.interview_round = interview_round
+ interview.job_applicant = doc.name
+ interview.designation = doc.designation
+ interview.resume_link = doc.resume_link
+ interview.job_opening = doc.job_title
+ interviewer_detail = get_interviewers(interview_round)
+
+ for d in interviewer_detail:
+ 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_detail_map = {}
+
+ for detail in interview_details:
+ interview_detail_map[detail.name] = detail
+
+ return interview_detail_map
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
new file mode 100644
index 0000000..c286787
--- /dev/null
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
@@ -0,0 +1,44 @@
+
+{% if not jQuery.isEmptyObject(data) %}
+
+<table class="table table-bordered small">
+ <thead>
+ <tr>
+ <th style="width: 16%" class="text-left">{{ __("Interview") }}</th>
+ <th style="width: 16%" class="text-left">{{ __("Interview Round") }}</th>
+ <th style="width: 12%" class="text-left">{{ __("Status") }}</th>
+ <th style="width: 14%" class="text-left">{{ __("Expected Rating") }}</th>
+ <th style="width: 10%" class="text-left">{{ __("Rating") }}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for(const [key, value] of Object.entries(data)) { %}
+ <tr>
+ <td class="text-left"> {%= key %} </td>
+ <td class="text-left"> {%= value["interview_round"] %} </td>
+ <td class="text-left"> {%= value["status"] %} </td>
+ <td class="text-left">
+ {% for (i = 0; i < value["expected_average_rating"]; i++) { %}
+ <span class="fa fa-star " style="color: #F6C35E;"></span>
+ {% } %}
+ {% for (i = 0; i < (5-value["expected_average_rating"]); i++) { %}
+ <span class="fa fa-star " style="color: #E7E9EB;"></span>
+ {% } %}
+ </td>
+ <td class="text-left">
+ {% if(value["average_rating"]){ %}
+ {% for (i = 0; i < value["average_rating"]; i++) { %}
+ <span class="fa fa-star " style="color: #F6C35E;"></span>
+ {% } %}
+ {% for (i = 0; i < (5-value["average_rating"]); i++) { %}
+ <span class="fa fa-star " style="color: #E7E9EB;"></span>
+ {% } %}
+ {% } %}
+ </td>
+ </tr>
+ {% } %}
+ </tbody>
+</table>
+{% else %}
+<p style="margin-top: 30px;"> No Interview has been scheduled.</p>
+{% endif %}
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
index 7f13115..2f7795f 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
@@ -1,15 +1,18 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
- return {
- 'fieldname': 'job_applicant',
- 'transactions': [
- {
- 'items': ['Employee', 'Employee Onboarding']
- },
- {
- 'items': ['Job Offer']
- },
- ],
- }
\ No newline at end of file
+ return {
+ '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.js b/erpnext/hr/doctype/job_applicant/test_job_applicant.js
index b5391c8..741a182 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.js
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.js
@@ -26,4 +26,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
index 8728342..8fc1290 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
@@ -3,10 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-# test_records = frappe.get_test_records('Job Applicant')
+import frappe
+
+from erpnext.hr.doctype.designation.test_designation import create_designation
+
class TestJobApplicant(unittest.TestCase):
pass
@@ -24,7 +26,8 @@
job_applicant = frappe.get_doc({
"doctype": "Job Applicant",
- "status": args.status or "Open"
+ "status": args.status or "Open",
+ "designation": create_designation().name
})
job_applicant.update(filters)
diff --git a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py b/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py
index 5f543d2..9139584 100644
--- a/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py
+++ b/erpnext/hr/doctype/job_applicant_source/job_applicant_source.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class JobApplicantSource(Document):
pass
diff --git a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.js b/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.js
deleted file mode 100644
index c093928..0000000
--- a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Job Applicant Source", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Job Applicant Source
- () => frappe.tests.make('Job Applicant Source', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py b/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py
index f318df2..0c29124 100644
--- a/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py
+++ b/erpnext/hr/doctype/job_applicant_source/test_job_applicant_source.py
@@ -5,5 +5,6 @@
import unittest
+
class TestJobApplicantSource(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py
index 7e650f7..07a7809 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -2,13 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cint
+from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
-from frappe import _
+from frappe.utils import cint
from frappe.utils.data import get_link_to_form
+
class JobOffer(Document):
def onload(self):
employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") or ""
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.js b/erpnext/hr/doctype/job_offer/test_job_offer.js
index c9d7d2b..5339b9c 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.js
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.js
@@ -48,4 +48,4 @@
() => frappe.timeout(2),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index b3e1dc8..162b245 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -2,11 +2,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, add_days
-from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+
+import frappe
+from frappe.utils import add_days, nowdate
+
from erpnext.hr.doctype.designation.test_designation import create_designation
+from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company
# test_records = frappe.get_test_records('Job Offer')
@@ -30,6 +32,7 @@
self.assertTrue(frappe.db.exists("Job Offer", job_offer.name))
def test_job_applicant_update(self):
+ frappe.db.set_value("HR Settings", None, "check_vacancies", 0)
create_staffing_plan()
job_applicant = create_job_applicant(email_id="test_job_applicants@example.com")
job_offer = create_job_offer(job_applicant=job_applicant.name)
@@ -41,7 +44,11 @@
job_offer.status = "Rejected"
job_offer.submit()
job_applicant.reload()
- self.assertEqual(job_applicant.status, "Rejected")
+ self.assertEquals(job_applicant.status, "Rejected")
+ frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
+
+ def tearDown(self):
+ frappe.db.sql("DELETE FROM `tabJob Offer` WHERE 1")
def create_job_offer(**args):
args = frappe._dict(args)
@@ -79,4 +86,4 @@
})
staffing_plan.insert()
staffing_plan.submit()
- return staffing_plan
\ No newline at end of file
+ return staffing_plan
diff --git a/erpnext/hr/doctype/job_offer_term/job_offer_term.py b/erpnext/hr/doctype/job_offer_term/job_offer_term.py
index 6dbe675..573cc6a 100644
--- a/erpnext/hr/doctype/job_offer_term/job_offer_term.py
+++ b/erpnext/hr/doctype/job_offer_term/job_offer_term.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class JobOfferTerm(Document):
pass
diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py
index 1e89767..38d9a71 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.py
+++ b/erpnext/hr/doctype/job_opening/job_opening.py
@@ -4,11 +4,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.website.website_generator import WebsiteGenerator
+import frappe
from frappe import _
-from erpnext.hr.doctype.staffing_plan.staffing_plan import get_designation_counts, get_active_staffing_plan_details
+from frappe.website.website_generator import WebsiteGenerator
+
+from erpnext.hr.doctype.staffing_plan.staffing_plan import (
+ get_active_staffing_plan_details,
+ get_designation_counts,
+)
+
class JobOpening(WebsiteGenerator):
website = frappe._dict(
diff --git a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
index c0890b4..a13e2a7 100644
--- a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
+++ b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Job Applicant']
}
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html
index c015101..69bf49b 100644
--- a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html
+++ b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html
@@ -1,16 +1,16 @@
<div class="my-5">
<h3>{{ doc.job_title }}</h3>
<p>{{ doc.description }}</p>
- {%- if doc.publish_salary_range -%}
+ {%- if doc.publish_salary_range -%}
<p><b>{{_("Salary range per month")}}: </b>{{ frappe.format_value(frappe.utils.flt(doc.lower_range), currency=doc.currency) }} - {{ frappe.format_value(frappe.utils.flt(doc.upper_range), currency=doc.currency) }}</p>
{% endif %}
<div>
{%- if doc.job_application_route -%}
- <a class='btn btn-primary'
+ <a class='btn btn-primary'
href='/{{doc.job_application_route}}?new=1&job_title={{ doc.name }}'>
{{ _("Apply Now") }}</a>
{% else %}
- <a class='btn btn-primary'
+ <a class='btn btn-primary'
href='/job_application?new=1&job_title={{ doc.name }}'>
{{ _("Apply Now") }}</a>
{% endif %}
diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.js b/erpnext/hr/doctype/job_opening/test_job_opening.js
index b9e6c0a..cc2f027 100644
--- a/erpnext/hr/doctype/job_opening/test_job_opening.js
+++ b/erpnext/hr/doctype/job_opening/test_job_opening.js
@@ -24,4 +24,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.py b/erpnext/hr/doctype/job_opening/test_job_opening.py
index 815ce5b..a66975c 100644
--- a/erpnext/hr/doctype/job_opening/test_job_opening.py
+++ b/erpnext/hr/doctype/job_opening/test_job_opening.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Job Opening')
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
index e9e129c..9742387 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
@@ -1,14 +1,14 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-cur_frm.add_fetch('employee','employee_name','employee_name');
+cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
frappe.ui.form.on("Leave Allocation", {
onload: function(frm) {
// Ignore cancellation of doctype on cancel all.
frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"];
- if(!frm.doc.from_date) frm.set_value("from_date", frappe.datetime.get_today());
+ if (!frm.doc.from_date) frm.set_value("from_date", frappe.datetime.get_today());
frm.set_query("employee", function() {
return {
@@ -25,9 +25,9 @@
},
refresh: function(frm) {
- if(frm.doc.docstatus === 1 && frm.doc.expired) {
+ if (frm.doc.docstatus === 1 && frm.doc.expired) {
var valid_expiry = moment(frappe.datetime.get_today()).isBetween(frm.doc.from_date, frm.doc.to_date);
- if(valid_expiry) {
+ if (valid_expiry) {
// expire current allocation
frm.add_custom_button(__('Expire Allocation'), function() {
frm.trigger("expire_allocation");
@@ -44,8 +44,8 @@
'expiry_date': frappe.datetime.get_today()
},
freeze: true,
- callback: function(r){
- if(!r.exc){
+ callback: function(r) {
+ if (!r.exc) {
frappe.msgprint(__("Allocation Expired!"));
}
frm.refresh();
@@ -77,8 +77,8 @@
},
leave_policy: function(frm) {
- if(frm.doc.leave_policy && frm.doc.leave_type) {
- frappe.db.get_value("Leave Policy Detail",{
+ if (frm.doc.leave_policy && frm.doc.leave_type) {
+ frappe.db.get_value("Leave Policy Detail", {
'parent': frm.doc.leave_policy,
'leave_type': frm.doc.leave_type
}, 'annual_allocation', (r) => {
@@ -91,13 +91,41 @@
return frappe.call({
method: "set_total_leaves_allocated",
doc: frm.doc,
- callback: function(r) {
+ callback: function() {
frm.refresh_fields();
}
- })
+ });
} else if (cint(frm.doc.carry_forward) == 0) {
frm.set_value("unused_leaves", 0);
frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated));
}
}
-});
\ No newline at end of file
+});
+
+frappe.tour["Leave Allocation"] = [
+ {
+ fieldname: "employee",
+ title: "Employee",
+ description: __("Select the Employee for which you want to allocate leaves.")
+ },
+ {
+ fieldname: "leave_type",
+ title: "Leave Type",
+ description: __("Select the Leave Type like Sick leave, Privilege Leave, Casual Leave, etc.")
+ },
+ {
+ fieldname: "from_date",
+ title: "From Date",
+ description: __("Select the date from which this Leave Allocation will be valid.")
+ },
+ {
+ fieldname: "to_date",
+ title: "To Date",
+ description: __("Select the date after which this Leave Allocation will expire.")
+ },
+ {
+ fieldname: "new_leaves_allocated",
+ title: "New Leaves Allocated",
+ description: __("Enter the number of leaves you want to allocate for the period.")
+ }
+];
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
index 3a6539e..52ee463 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
@@ -219,7 +219,8 @@
"fieldname": "leave_policy_assignment",
"fieldtype": "Link",
"label": "Leave Policy Assignment",
- "options": "Leave Policy Assignment"
+ "options": "Leave Policy Assignment",
+ "read_only": 1
},
{
"fetch_from": "employee.company",
@@ -236,7 +237,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-06-03 15:28:26.335104",
+ "modified": "2021-10-01 15:28:26.335104",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Allocation",
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 4757cd3..e4886d7 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -2,13 +2,19 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt, date_diff, formatdate, add_days, today, getdate
from frappe import _
from frappe.model.document import Document
-from erpnext.hr.utils import set_employee_name, get_leave_period
-from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation, create_leave_ledger_entry
+from frappe.utils import add_days, date_diff, flt, formatdate, getdate
+
from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period
+from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import (
+ create_leave_ledger_entry,
+ expire_allocation,
+)
+from erpnext.hr.utils import get_leave_period, set_employee_name
+
class OverlapError(frappe.ValidationError): pass
class BackDatedAllocationError(frappe.ValidationError): pass
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
index 7456aeb..84423bd 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -17,4 +17,4 @@
'items': ['Employee Leave Balance']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
index 0ef78f2..d5364fc 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js
@@ -38,4 +38,4 @@
"total leave calculation is correctly set"),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index fdcd533..b185056 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -1,10 +1,15 @@
from __future__ import unicode_literals
-import frappe
-import erpnext
+
import unittest
-from frappe.utils import nowdate, add_months, getdate, add_days
+
+import frappe
+from frappe.utils import add_days, add_months, getdate, nowdate
+
+import erpnext
+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.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
+
+
class TestLeaveAllocation(unittest.TestCase):
@classmethod
def setUpClass(cls):
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 9ccb915..9e8cb55 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -1,8 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-cur_frm.add_fetch('employee','employee_name','employee_name');
-cur_frm.add_fetch('employee','company','company');
+cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
+cur_frm.add_fetch('employee', 'company', 'company');
frappe.ui.form.on("Leave Application", {
setup: function(frm) {
@@ -19,7 +19,6 @@
frm.set_query("employee", erpnext.queries.employee);
},
onload: function(frm) {
-
// Ignore cancellation of doctype on cancel all.
frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"];
@@ -42,9 +41,9 @@
},
validate: function(frm) {
- if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){
+ if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1) {
frm.doc.half_day_date = frm.doc.from_date;
- }else if (frm.doc.half_day == 0){
+ } else if (frm.doc.half_day == 0) {
frm.doc.half_day_date = "";
}
frm.toggle_reqd("half_day_date", frm.doc.half_day == 1);
@@ -79,14 +78,14 @@
__("Allocated Leaves")
);
frm.dashboard.show();
- let allowed_leave_types = Object.keys(leave_details);
+ let allowed_leave_types = Object.keys(leave_details);
// lwps should be allowed, lwps don't have any allocation
allowed_leave_types = allowed_leave_types.concat(lwps);
- frm.set_query('leave_type', function(){
+ frm.set_query('leave_type', function() {
return {
- filters : [
+ filters: [
['leave_type_name', 'in', allowed_leave_types]
]
};
@@ -99,7 +98,7 @@
frm.trigger("calculate_total_days");
}
cur_frm.set_intro("");
- if(frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) {
+ if (frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) {
frm.set_intro(__("Fill the form and save it"));
}
@@ -118,7 +117,7 @@
},
leave_approver: function(frm) {
- if(frm.doc.leave_approver){
+ if (frm.doc.leave_approver) {
frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
}
},
@@ -131,12 +130,10 @@
if (frm.doc.half_day) {
if (frm.doc.from_date == frm.doc.to_date) {
frm.set_value("half_day_date", frm.doc.from_date);
- }
- else {
+ } else {
frm.trigger("half_day_datepicker");
}
- }
- else {
+ } else {
frm.set_value("half_day_date", "");
}
frm.trigger("calculate_total_days");
@@ -163,11 +160,11 @@
half_day_datepicker.update({
minDate: frappe.datetime.str_to_obj(frm.doc.from_date),
maxDate: frappe.datetime.str_to_obj(frm.doc.to_date)
- })
+ });
},
get_leave_balance: function(frm) {
- if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
+ if (frm.doc.docstatus === 0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
return frappe.call({
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on",
args: {
@@ -177,11 +174,10 @@
leave_type: frm.doc.leave_type,
consider_all_leaves_in_the_allocation_period: true
},
- callback: function(r) {
+ callback: function (r) {
if (!r.exc && r.message) {
frm.set_value('leave_balance', r.message);
- }
- else {
+ } else {
frm.set_value('leave_balance', "0");
}
}
@@ -190,12 +186,12 @@
},
calculate_total_days: function(frm) {
- if(frm.doc.from_date && frm.doc.to_date && frm.doc.employee && frm.doc.leave_type) {
+ if (frm.doc.from_date && frm.doc.to_date && frm.doc.employee && frm.doc.leave_type) {
var from_date = Date.parse(frm.doc.from_date);
var to_date = Date.parse(frm.doc.to_date);
- if(to_date < from_date){
+ if (to_date < from_date) {
frappe.msgprint(__("To Date cannot be less than From Date"));
frm.set_value('to_date', '');
return;
@@ -222,7 +218,7 @@
},
set_leave_approver: function(frm) {
- if(frm.doc.employee) {
+ if (frm.doc.employee) {
// server call is done to include holidays in leave days calculations
return frappe.call({
method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver',
@@ -238,3 +234,36 @@
}
}
});
+
+frappe.tour["Leave Application"] = [
+ {
+ fieldname: "employee",
+ title: "Employee",
+ description: __("Select the Employee.")
+ },
+ {
+ fieldname: "leave_type",
+ title: "Leave Type",
+ description: __("Select type of leave the employee wants to apply for, like Sick Leave, Privilege Leave, Casual Leave, etc.")
+ },
+ {
+ fieldname: "from_date",
+ title: "From Date",
+ description: __("Select the start date for your Leave Application.")
+ },
+ {
+ fieldname: "to_date",
+ title: "To Date",
+ description: __("Select the end date for your Leave Application.")
+ },
+ {
+ fieldname: "half_day",
+ title: "Half Day",
+ description: __("To apply for a Half Day check 'Half Day' and select the Half Day Date")
+ },
+ {
+ fieldname: "leave_approver",
+ title: "Leave Approver",
+ description: __("Select your Leave Approver i.e. the person who approves or rejects your leaves.")
+ }
+];
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 93fb19f..349ed7a 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -2,14 +2,33 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate
-from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver, validate_active_employee
-from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
-from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from frappe.utils import (
+ add_days,
+ cint,
+ cstr,
+ date_diff,
+ flt,
+ formatdate,
+ get_fullname,
+ get_link_to_form,
+ getdate,
+ nowdate,
+)
+
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
+from erpnext.hr.utils import (
+ get_leave_period,
+ set_employee_name,
+ share_doc_with_approver,
+ validate_active_employee,
+)
+
class LeaveDayBlockedError(frappe.ValidationError): pass
class OverlapError(frappe.ValidationError): pass
@@ -17,6 +36,8 @@
class NotAnOptionalHoliday(frappe.ValidationError): pass
from frappe.model.document import Document
+
+
class LeaveApplication(Document):
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
@@ -55,6 +76,7 @@
# notify leave applier about approval
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
self.notify_employee()
+
self.create_leave_ledger_entry()
self.reload()
@@ -87,7 +109,13 @@
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")
- if allowed_role not in frappe.get_roles():
+ 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")))
+
+ 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)):
@@ -662,26 +690,30 @@
@frappe.whitelist()
def get_events(start, end, filters=None):
+ from frappe.desk.reportview import get_filters_cond
events = []
- employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}, ["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=''
- 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("Leave Application", filters, [])
# show department leaves for employee
if "Employee" in frappe.get_roles():
add_department_leaves(events, start, end, employee, company)
add_leaves(events, start, end, conditions)
-
add_block_dates(events, start, end, employee, company)
add_holidays(events, start, end, employee, company)
+
return events
def add_department_leaves(events, start, end, employee, company):
@@ -697,26 +729,37 @@
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")):
- from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("Leave Application")
if match_conditions:
conditions.append(match_conditions)
- query = """select name, from_date, to_date, employee_name, half_day,
- status, employee, docstatus
- from `tabLeave Application` where
- from_date <= %(end)s and to_date >= %(start)s <= to_date
- and docstatus < 2
- and status!='Rejected' """
+ query = """SELECT
+ docstatus,
+ name,
+ employee,
+ employee_name,
+ leave_type,
+ from_date,
+ to_date,
+ half_day,
+ status,
+ color
+ FROM `tabLeave Application`
+ WHERE
+ from_date <= %(end)s AND to_date >= %(start)s <= to_date
+ AND docstatus < 2
+ AND status != 'Rejected'
+ """
if conditions:
- query += ' and ' + ' and '.join(conditions)
+ query += ' AND ' + ' AND '.join(conditions)
if filter_conditions:
query += filter_conditions
@@ -729,11 +772,13 @@
"to_date": d.to_date,
"docstatus": d.docstatus,
"color": d.color,
- "title": cstr(d.employee_name) + (' ' + _('(Half Day)') if d.half_day else ''),
+ "all_day": int(not d.half_day),
+ "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)
+
def add_block_dates(events, start, end, employee, company):
# block days
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
diff --git a/erpnext/hr/doctype/leave_application/leave_application_calendar.js b/erpnext/hr/doctype/leave_application/leave_application_calendar.js
index 0286f30..0ba0285 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_calendar.js
+++ b/erpnext/hr/doctype/leave_application/leave_application_calendar.js
@@ -7,7 +7,9 @@
"end": "to_date",
"id": "name",
"title": "title",
- "docstatus": 1
+ "docstatus": 1,
+ "color": "color",
+ "allDay": "all_day"
},
options: {
header: {
@@ -17,4 +19,4 @@
}
},
get_events_method: "erpnext.hr.doctype.leave_application.leave_application.get_events"
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
index c1d6a66..c45717f 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py
@@ -17,4 +17,4 @@
'items': ['Employee Leave Balance']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_application/leave_application_email_template.html b/erpnext/hr/doctype/leave_application/leave_application_email_template.html
index 209302e..14ca41b 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_email_template.html
+++ b/erpnext/hr/doctype/leave_application/leave_application_email_template.html
@@ -21,5 +21,5 @@
<tr>
<td>Status</td>
<td>{{status}}</td>
- </tr>
+ </tr>
</table>
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.js b/erpnext/hr/doctype/leave_application/test_leave_application.js
index 6d7b6a7..0866b0b 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.js
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.js
@@ -39,4 +39,4 @@
// "leave for correct employee is submitted"),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index 2832e2f..629b20e 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -2,16 +2,24 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on
+import frappe
from frappe.permissions import clear_user_permissions_for_doctype
-from frappe.utils import add_days, nowdate, now_datetime, getdate, add_months
-from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
-from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
-from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
+from frappe.utils import add_days, add_months, getdate, nowdate
+
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_application.leave_application import (
+ LeaveDayBlockedError,
+ NotAnOptionalHoliday,
+ OverlapError,
+ get_leave_balance_on,
+)
+from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
+ create_assignment_for_multiple_employees,
+)
+from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
@@ -113,6 +121,7 @@
application = self.get_application(_test_records[0])
application.insert()
+ application.reload()
application.status = "Approved"
self.assertRaises(LeaveDayBlockedError, application.submit)
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 9cb9fc0..9ba079c 100644
--- a/erpnext/hr/doctype/leave_block_list/leave_block_list.py
+++ b/erpnext/hr/doctype/leave_block_list/leave_block_list.py
@@ -4,10 +4,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class LeaveBlockList(Document):
def validate(self):
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 2aa5498..30e7572 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,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'leave_block_list',
@@ -8,4 +9,4 @@
'items': ['Department']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
index 4537878..b39601b 100644
--- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
+++ b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js
@@ -24,4 +24,4 @@
'name of blocked leave list correctly saved'),
() => done()
]);
-});
\ No newline at end of file
+});
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 0eb69a5..dd90e4f 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
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
from frappe.utils import getdate
+
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
+
class TestLeaveBlockList(unittest.TestCase):
def tearDown(self):
frappe.set_user("Administrator")
diff --git a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py b/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py
index be06b76..2f64847 100644
--- a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py
+++ b/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class LeaveBlockListAllow(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py b/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py
index f4028f5..4a8f45d 100644
--- a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py
+++ b/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class LeaveBlockListDate(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js
index b60e225..4a45080 100644
--- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js
+++ b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js
@@ -21,4 +21,4 @@
});
}
}
-});
\ No newline at end of file
+});
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 7401402..681a5e2 100644
--- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
+++ b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint, cstr, flt, nowdate, comma_and, date_diff
-from frappe import msgprint, _
+import frappe
+from frappe import _, msgprint
from frappe.model.document import Document
+from frappe.utils import cint, comma_and, cstr, flt
+
class LeaveControlPanel(Document):
def get_employees(self):
@@ -51,7 +52,7 @@
la.docstatus = 1
la.save()
leave_allocated_for.append(d[0])
- except:
+ except Exception:
pass
if leave_allocated_for:
msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for)))
diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
index 2b5cec1..9d37327 100644
--- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
+++ b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js
@@ -47,4 +47,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py
index 9a907c8..f64b233 100644
--- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py
+++ b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestLeaveControlPanel(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 912bd8a..7656abf 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -3,14 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import getdate, nowdate, flt
-from erpnext.hr.utils import set_employee_name, validate_active_employee
-from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
-from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
+from frappe.utils import getdate, nowdate
+
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
+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 (
+ get_assigned_salary_structure,
+)
+
class LeaveEncashment(Document):
def validate(self):
@@ -134,4 +139,4 @@
leave_type=allocation.leave_type,
encashment_date=allocation.to_date
))
- leave_encashment.insert(ignore_permissions=True)
\ No newline at end of file
+ leave_encashment.insert(ignore_permissions=True)
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.js b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.js
deleted file mode 100644
index cafd960..0000000
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Encashment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Leave Encashment
- () => frappe.tests.make('Leave Encashment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
index c1da8b4..762745b 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -3,14 +3,18 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import today, add_months
+
+import frappe
+from frappe.utils import add_months, today
+
from erpnext.hr.doctype.employee.test_employee import make_employee
-from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
-from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
-from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy\
+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_structure.test_salary_structure import make_salary_structure
test_dependencies = ["Leave Type"]
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 cf13036..6cf9685 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
-from frappe.utils import add_days, today, flt, DATE_FORMAT, getdate
+from frappe.model.document import Document
+from frappe.utils import DATE_FORMAT, flt, getdate, today
+
class LeaveLedgerEntry(Document):
def validate(self):
@@ -185,4 +187,4 @@
from_date=allocation.to_date,
to_date=allocation.to_date
)
- create_leave_ledger_entry(allocation, args)
\ No newline at end of file
+ create_leave_ledger_entry(allocation, args)
diff --git a/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py
index 6f7725c..5fa419d 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py
+++ b/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLeaveLedgerEntry(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py
index 28a33f6..143d23a 100644
--- a/erpnext/hr/doctype/leave_period/leave_period.py
+++ b/erpnext/hr/doctype/leave_period/leave_period.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate, cstr, add_days, date_diff, getdate, ceil
from frappe.model.document import Document
+from frappe.utils import getdate
+
from erpnext.hr.utils import validate_overlap
-from frappe.utils.background_jobs import enqueue
+
class LeavePeriod(Document):
diff --git a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
index 1572de3..a64c63a 100644
--- a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
+++ b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'leave_period',
@@ -10,4 +12,4 @@
'items': ['Leave Allocation']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.js b/erpnext/hr/doctype/leave_period/test_leave_period.js
deleted file mode 100644
index ec0a809..0000000
--- a/erpnext/hr/doctype/leave_period/test_leave_period.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Period", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Leave Period
- () => frappe.tests.make('Leave Period', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py
index b5857bc..5c5ae13 100644
--- a/erpnext/hr/doctype/leave_period/test_leave_period.py
+++ b/erpnext/hr/doctype/leave_period/test_leave_period.py
@@ -3,9 +3,12 @@
# See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
import unittest
+import frappe
+
+import erpnext
+
test_dependencies = ["Employee", "Leave Type", "Leave Policy"]
class TestLeavePeriod(unittest.TestCase):
@@ -27,4 +30,4 @@
"to_date": to_date,
"is_active": 1
}).insert()
- return leave_period
\ No newline at end of file
+ return leave_period
diff --git a/erpnext/hr/doctype/leave_policy/leave_policy.py b/erpnext/hr/doctype/leave_policy/leave_policy.py
index 964a5de..b11459d 100644
--- a/erpnext/hr/doctype/leave_policy/leave_policy.py
+++ b/erpnext/hr/doctype/leave_policy/leave_policy.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class LeavePolicy(Document):
def validate(self):
if self.leave_policy_details:
diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
index ff7f042..76f886c 100644
--- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
+++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'leave_policy',
@@ -10,4 +12,4 @@
'items': ['Leave Policy Assignment', 'Leave Allocation']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.js b/erpnext/hr/doctype/leave_policy/test_leave_policy.js
deleted file mode 100644
index 5404a63..0000000
--- a/erpnext/hr/doctype/leave_policy/test_leave_policy.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Policy", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Leave Policy
- () => frappe.tests.make('Leave Policy', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.py b/erpnext/hr/doctype/leave_policy/test_leave_policy.py
index fc868ea..b0743f5 100644
--- a/erpnext/hr/doctype/leave_policy/test_leave_policy.py
+++ b/erpnext/hr/doctype/leave_policy/test_leave_policy.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestLeavePolicy(unittest.TestCase):
def test_max_leave_allowed(self):
random_leave_type = frappe.get_all("Leave Type", fields=["name", "max_leaves_allowed"])
@@ -28,4 +30,4 @@
"leave_type": args.leave_type or "_Test Leave Type",
"annual_allocation": args.annual_allocation or 10
}]
- })
\ No newline at end of file
+ })
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 d7cb1c8..f62b300 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
@@ -3,14 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-from frappe import _, bold
-from frappe.utils import getdate, date_diff, comma_and, formatdate, get_datetime, flt
-from math import ceil
+
import json
+from math import ceil
+
+import frappe
+from frappe import _, bold
+from frappe.model.document import Document
+from frappe.utils import date_diff, flt, formatdate, get_datetime, getdate
from six import string_types
+
class LeavePolicyAssignment(Document):
def validate(self):
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 4bb0535..79142a6 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'leave_policy_assignment',
@@ -10,4 +12,4 @@
'items': ['Leave Allocation']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js
index 8fe4b8f..8b954c4 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js
@@ -105,4 +105,4 @@
});
}
}
-};
\ No newline at end of file
+};
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 9a14e35..cbb26a1 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
@@ -3,11 +3,18 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.hr.doctype.leave_application.test_leave_application import get_leave_period, get_employee
-from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
+
+import frappe
+
+from erpnext.hr.doctype.leave_application.test_leave_application import (
+ get_employee,
+ get_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,
+)
test_dependencies = ["Employee"]
@@ -99,5 +106,3 @@
def tearDown(self):
for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]:
frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec
-
-
diff --git a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py b/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py
index c103f08..f889424 100644
--- a/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py
+++ b/erpnext/hr/doctype/leave_policy_detail/leave_policy_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LeavePolicyDetail(Document):
pass
diff --git a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.js b/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.js
deleted file mode 100644
index 1c8995b..0000000
--- a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Leave Policy Detail", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Leave Policy Detail
- () => frappe.tests.make('Leave Policy Detail', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py b/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py
index 610b1fa..4cf9db2 100644
--- a/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py
+++ b/erpnext/hr/doctype/leave_policy_detail/test_leave_policy_detail.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestLeavePolicyDetail(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/leave_type/leave_type.js b/erpnext/hr/doctype/leave_type/leave_type.js
index 8622309..b930ded 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.js
+++ b/erpnext/hr/doctype/leave_type/leave_type.js
@@ -2,3 +2,37 @@
refresh: function(frm) {
}
});
+
+
+frappe.tour["Leave Type"] = [
+ {
+ fieldname: "max_leaves_allowed",
+ title: "Maximum Leave Allocation Allowed",
+ description: __("This field allows you to set the maximum number of leaves that can be allocated annually for this Leave Type while creating the Leave Policy")
+ },
+ {
+ fieldname: "max_continuous_days_allowed",
+ title: "Maximum Consecutive Leaves Allowed",
+ description: __("This field allows you to set the maximum number of consecutive leaves an Employee can apply for.")
+ },
+ {
+ fieldname: "is_optional_leave",
+ title: "Is Optional Leave",
+ description: __("Optional Leaves are holidays that Employees can choose to avail from a list of holidays published by the company.")
+ },
+ {
+ fieldname: "is_compensatory",
+ title: "Is Compensatory Leave",
+ description: __("Leaves you can avail against a holiday you worked on. You can claim Compensatory Off Leave using Compensatory Leave request. Click") + " <a href='https://docs.erpnext.com/docs/v13/user/manual/en/human-resources/compensatory-leave-request' target='_blank'>here</a> " + __('to know more')
+ },
+ {
+ fieldname: "allow_encashment",
+ title: "Allow Encashment",
+ description: __("From here, you can enable encashment for the balance leaves.")
+ },
+ {
+ fieldname: "is_earned_leave",
+ title: "Is Earned Leaves",
+ description: __("Earned Leaves are leaves earned by an Employee after working with the company for a certain amount of time. Enabling this will allocate leaves on pro-rata basis by automatically updating Leave Allocation for leaves of this type at intervals set by 'Earned Leave Frequency.")
+ }
+];
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json
index 8f2ae6e..06ca4cd 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.json
+++ b/erpnext/hr/doctype/leave_type/leave_type.json
@@ -50,7 +50,7 @@
{
"fieldname": "max_leaves_allowed",
"fieldtype": "Int",
- "label": "Max Leaves Allowed"
+ "label": "Maximum Leave Allocation Allowed"
},
{
"fieldname": "applicable_after",
@@ -61,7 +61,7 @@
"fieldname": "max_continuous_days_allowed",
"fieldtype": "Int",
"in_list_view": 1,
- "label": "Maximum Continuous Days Applicable",
+ "label": "Maximum Consecutive Leaves Allowed",
"oldfieldname": "max_days_allowed",
"oldfieldtype": "Data"
},
@@ -87,6 +87,7 @@
},
{
"default": "0",
+ "description": "These leaves are holidays permitted by the company however, availing it is optional for an Employee.",
"fieldname": "is_optional_leave",
"fieldtype": "Check",
"label": "Is Optional Leave"
@@ -205,6 +206,7 @@
},
{
"depends_on": "eval:doc.is_ppl == 1",
+ "description": "For a day of leave taken, if you still pay (say) 50% of the daily salary, then enter 0.50 in this field.",
"fieldname": "fraction_of_daily_salary_per_leave",
"fieldtype": "Float",
"label": "Fraction of Daily Salary per Leave",
@@ -214,7 +216,7 @@
"icon": "fa fa-flag",
"idx": 1,
"links": [],
- "modified": "2021-08-12 16:10:36.464690",
+ "modified": "2021-10-02 11:59:40.503359",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Type",
diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py
index 21f180b..195c858 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.py
+++ b/erpnext/hr/doctype/leave_type/leave_type.py
@@ -2,13 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import calendar
-import frappe
-from datetime import datetime
-from frappe.utils import today
-from frappe import _
+import frappe
+from frappe import _
from frappe.model.document import Document
+from frappe.utils import today
+
class LeaveType(Document):
def validate(self):
diff --git a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
index 5cae9a8..773d4e8 100644
--- a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
+++ b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'leave_type',
@@ -11,4 +12,4 @@
'items': ['Attendance', 'Leave Encashment']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.js b/erpnext/hr/doctype/leave_type/test_leave_type.js
index d939a24..db910cd 100644
--- a/erpnext/hr/doctype/leave_type/test_leave_type.js
+++ b/erpnext/hr/doctype/leave_type/test_leave_type.js
@@ -19,4 +19,4 @@
'leave type correctly saved'),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py
index 7fef297..ee8db74 100644
--- a/erpnext/hr/doctype/leave_type/test_leave_type.py
+++ b/erpnext/hr/doctype/leave_type/test_leave_type.py
@@ -3,7 +3,6 @@
from __future__ import unicode_literals
import frappe
-from frappe import _
test_records = frappe.get_test_records('Leave Type')
@@ -28,4 +27,4 @@
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
\ No newline at end of file
+ return leave_type
diff --git a/erpnext/hr/doctype/offer_term/offer_term.py b/erpnext/hr/doctype/offer_term/offer_term.py
index 6a63201..5f8f591 100644
--- a/erpnext/hr/doctype/offer_term/offer_term.py
+++ b/erpnext/hr/doctype/offer_term/offer_term.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class OfferTerm(Document):
pass
diff --git a/erpnext/hr/doctype/offer_term/test_offer_term.py b/erpnext/hr/doctype/offer_term/test_offer_term.py
index d0dd14d..ec7edd4 100644
--- a/erpnext/hr/doctype/offer_term/test_offer_term.py
+++ b/erpnext/hr/doctype/offer_term/test_offer_term.py
@@ -2,7 +2,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Offer Term')
diff --git a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py b/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py
index 62f62a5..f66fd27 100644
--- a/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py
+++ b/erpnext/hr/doctype/purpose_of_travel/purpose_of_travel.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PurposeofTravel(Document):
pass
diff --git a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.js b/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.js
deleted file mode 100644
index 936c21c..0000000
--- a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Purpose of Travel", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Purpose of Travel
- () => frappe.tests.make('Purpose of Travel', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py b/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py
index ccd950d..b33f389 100644
--- a/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py
+++ b/erpnext/hr/doctype/purpose_of_travel/test_purpose_of_travel.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestPurposeofTravel(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 89ae4d5..05b74a0 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -3,14 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from datetime import datetime, timedelta
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate
+from frappe.utils import cstr, getdate, now_datetime, nowdate
+
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
-from datetime import timedelta, datetime
+
class ShiftAssignment(Document):
def validate(self):
@@ -135,7 +139,7 @@
return shift_timing_map
-def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None):
+def get_employee_shift(employee, for_date=None, consider_default_shift=False, next_shift_direction=None):
"""Returns a Shift Type for the given employee on the given date. (excluding the holidays)
:param employee: Employee for which shift is required.
@@ -143,6 +147,8 @@
:param consider_default_shift: If set to true, default shift is taken when no shift assignment is found.
:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
"""
+ if for_date is None:
+ for_date = nowdate()
default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
shift_type_name = None
shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date'])
@@ -196,9 +202,11 @@
return get_shift_details(shift_type_name, for_date)
-def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_default_shift=False):
+def get_employee_shift_timings(employee, for_timestamp=None, consider_default_shift=False):
"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
"""
+ if for_timestamp is None:
+ for_timestamp = now_datetime()
# write and verify a test case for midnight shift.
prev_shift = curr_shift = next_shift = None
curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
@@ -216,7 +224,7 @@
return prev_shift, curr_shift, next_shift
-def get_shift_details(shift_type_name, for_date=nowdate()):
+def get_shift_details(shift_type_name, for_date=None):
"""Returns Shift Details which contain some additional information as described below.
'shift_details' contains the following keys:
'shift_type' - Object of DocType Shift Type,
@@ -230,6 +238,8 @@
"""
if not shift_type_name:
return None
+ if not for_date:
+ for_date = nowdate()
shift_type = frappe.get_doc('Shift Type', shift_type_name)
start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time
for_date = for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
index bb692e1..5d2360f 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
@@ -10,4 +10,4 @@
"allDay": "allDay",
},
get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events"
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.js b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.js
deleted file mode 100644
index 7727287..0000000
--- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shift Assignment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shift Assignment
- () => frappe.tests.make('Shift Assignment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
index 4c3c1ed..84003e2 100644
--- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, add_days
+
+import frappe
+from frappe.utils import add_days, nowdate
test_dependencies = ["Shift Type"]
@@ -77,4 +78,4 @@
"status": 'Active'
})
- self.assertRaises(frappe.ValidationError, shift_assignment_3.save)
\ No newline at end of file
+ self.assertRaises(frappe.ValidationError, shift_assignment_3.save)
diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py
index 6461f07..a6ac7c8 100644
--- a/erpnext/hr/doctype/shift_request/shift_request.py
+++ b/erpnext/hr/doctype/shift_request/shift_request.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import formatdate, getdate
+
from erpnext.hr.utils import share_doc_with_approver, validate_active_employee
+
class OverlapError(frappe.ValidationError): pass
class ShiftRequest(Document):
@@ -94,4 +97,4 @@
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)
\ No newline at end of file
+ frappe.throw(msg, OverlapError)
diff --git a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
index e3bf5df..3ceafc0 100644
--- a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
+++ b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Shift Assignment']
},
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.js b/erpnext/hr/doctype/shift_request/test_shift_request.js
deleted file mode 100644
index 9c8cd70..0000000
--- a/erpnext/hr/doctype/shift_request/test_shift_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shift Request", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shift Request
- () => frappe.tests.make('Shift Request', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py
index 3525540..7b4a3ca 100644
--- a/erpnext/hr/doctype/shift_request/test_shift_request.py
+++ b/erpnext/hr/doctype/shift_request/test_shift_request.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, add_days
+
+import frappe
+from frappe.utils import add_days, nowdate
+
from erpnext.hr.doctype.employee.test_employee import make_employee
test_dependencies = ["Shift Type"]
@@ -106,4 +108,4 @@
return shift_request
shift_request.submit()
- return shift_request
\ No newline at end of file
+ return shift_request
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index d5fdda8..e53373d 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -3,16 +3,25 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import itertools
from datetime import timedelta
import frappe
from frappe.model.document import Document
-from frappe.utils import cint, getdate, get_datetime
-from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift, get_employee_shift
-from erpnext.hr.doctype.employee_checkin.employee_checkin import mark_attendance_and_link_log, calculate_working_hours
+from frappe.utils import cint, get_datetime, getdate
+
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.shift_assignment.shift_assignment import (
+ get_actual_start_end_datetime_of_shift,
+ get_employee_shift,
+)
+
class ShiftType(Document):
@frappe.whitelist()
diff --git a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
index aedd190..b78c69a 100644
--- a/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
+++ b/erpnext/hr/doctype/shift_type/shift_type_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
diff --git a/erpnext/hr/doctype/shift_type/test_shift_type.js b/erpnext/hr/doctype/shift_type/test_shift_type.js
deleted file mode 100644
index 846f931..0000000
--- a/erpnext/hr/doctype/shift_type/test_shift_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shift Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shift Type
- () => frappe.tests.make('Shift Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/shift_type/test_shift_type.py b/erpnext/hr/doctype/shift_type/test_shift_type.py
index bc4f0ea..699030f 100644
--- a/erpnext/hr/doctype/shift_type/test_shift_type.py
+++ b/erpnext/hr/doctype/shift_type/test_shift_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestShiftType(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/skill/skill.py b/erpnext/hr/doctype/skill/skill.py
index 8d24212..ebaa410 100644
--- a/erpnext/hr/doctype/skill/skill.py
+++ b/erpnext/hr/doctype/skill/skill.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class Skill(Document):
pass
diff --git a/erpnext/healthcare/doctype/patient_assessment/__init__.py b/erpnext/hr/doctype/skill_assessment/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/patient_assessment/__init__.py
rename to erpnext/hr/doctype/skill_assessment/__init__.py
diff --git a/erpnext/hr/doctype/skill_assessment/skill_assessment.json b/erpnext/hr/doctype/skill_assessment/skill_assessment.json
new file mode 100644
index 0000000..8b935c4
--- /dev/null
+++ b/erpnext/hr/doctype/skill_assessment/skill_assessment.json
@@ -0,0 +1,41 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 17:07:39.656289",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "skill",
+ "rating"
+ ],
+ "fields": [
+ {
+ "fieldname": "skill",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Skill",
+ "options": "Skill",
+ "read_only": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "rating",
+ "fieldtype": "Rating",
+ "in_list_view": 1,
+ "label": "Rating",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-12 17:18:14.032298",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Skill Assessment",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/skill_assessment/skill_assessment.py b/erpnext/hr/doctype/skill_assessment/skill_assessment.py
new file mode 100644
index 0000000..3b74c4e
--- /dev/null
+++ b/erpnext/hr/doctype/skill_assessment/skill_assessment.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SkillAssessment(Document):
+ pass
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index e6c783a..93cd4e1 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -3,12 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
-from frappe.utils import getdate, nowdate, cint, flt
+from frappe.model.document import Document
+from frappe.utils import cint, flt, getdate, nowdate
from frappe.utils.nestedset import get_descendants_of
+
class SubsidiaryCompanyError(frappe.ValidationError): pass
class ParentCompanyError(frappe.ValidationError): pass
@@ -153,7 +155,11 @@
return employee_counts
@frappe.whitelist()
-def get_active_staffing_plan_details(company, designation, from_date=getdate(nowdate()), to_date=getdate(nowdate())):
+def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None):
+ if from_date is None:
+ from_date = getdate(nowdate())
+ if to_date is None:
+ to_date = getdate(nowdate())
if not company or not designation:
frappe.throw(_("Please select Company and Designation"))
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
index 35a303f..24ae122 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Job Opening']
}
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.js b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.js
deleted file mode 100644
index 64320bc..0000000
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Staffing Plan", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Staffing Plan
- () => frappe.tests.make('Staffing Plan', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
index 628255b..4517cba 100644
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
@@ -3,11 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.hr.doctype.staffing_plan.staffing_plan import SubsidiaryCompanyError
-from erpnext.hr.doctype.staffing_plan.staffing_plan import ParentCompanyError
-from frappe.utils import nowdate, add_days
+
+import frappe
+from frappe.utils import add_days, nowdate
+
+from erpnext.hr.doctype.staffing_plan.staffing_plan import (
+ ParentCompanyError,
+ SubsidiaryCompanyError,
+)
test_dependencies = ["Designation"]
@@ -94,4 +98,4 @@
company.parent_company = "_Test Company 3"
company.default_currency = "INR"
company.country = "Pakistan"
- company.insert()
\ No newline at end of file
+ company.insert()
diff --git a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py
index 28a651e..ea89df3 100644
--- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py
+++ b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StaffingPlanDetail(Document):
pass
diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py
index 9b32136..ed44fa6 100644
--- a/erpnext/hr/doctype/training_event/test_training_event.py
+++ b/erpnext/hr/doctype/training_event/test_training_event.py
@@ -3,11 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import today, add_days
+
+import frappe
+from frappe.utils import add_days, today
+
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee
+
class TestTrainingEvent(unittest.TestCase):
def setUp(self):
create_training_program("Basic Training")
@@ -58,4 +61,4 @@
"end_time": add_days(today(), 6),
"introduction": "Welcome to the Basic Training Event",
"employees": attendees
- }).insert()
\ No newline at end of file
+ }).insert()
diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event.js b/erpnext/hr/doctype/training_event/tests/test_training_event.js
index 8ff4fec..08031a1 100644
--- a/erpnext/hr/doctype/training_event/tests/test_training_event.js
+++ b/erpnext/hr/doctype/training_event/tests/test_training_event.js
@@ -56,4 +56,4 @@
() => frappe.timeout(2),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js
index d5f6e5f..642e6a1 100644
--- a/erpnext/hr/doctype/training_event/training_event.js
+++ b/erpnext/hr/doctype/training_event/training_event.js
@@ -46,4 +46,3 @@
frm.events.set_employee_query(frm);
}
});
-
diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py
index e2c30cb..9b01d3d 100644
--- a/erpnext/hr/doctype/training_event/training_event.py
+++ b/erpnext/hr/doctype/training_event/training_event.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
from frappe.utils import time_diff_in_seconds
+
from erpnext.hr.doctype.employee.employee import get_employee_emails
+
class TrainingEvent(Document):
def validate(self):
self.set_employee_emails()
diff --git a/erpnext/hr/doctype/training_event/training_event_dashboard.py b/erpnext/hr/doctype/training_event/training_event_dashboard.py
index 1c1645c..a917c87 100644
--- a/erpnext/hr/doctype/training_event/training_event_dashboard.py
+++ b/erpnext/hr/doctype/training_event/training_event_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Training Result', 'Training Feedback']
},
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.py b/erpnext/hr/doctype/training_event_employee/training_event_employee.py
index 234e958..0892355 100644
--- a/erpnext/hr/doctype/training_event_employee/training_event_employee.py
+++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TrainingEventEmployee(Document):
pass
diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.js b/erpnext/hr/doctype/training_feedback/test_training_feedback.js
index 9daa51f..5c825ae 100644
--- a/erpnext/hr/doctype/training_feedback/test_training_feedback.js
+++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.js
@@ -49,4 +49,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
index c30a3ad..a9bf6d6 100644
--- a/erpnext/hr/doctype/training_feedback/test_training_feedback.py
+++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
@@ -3,10 +3,17 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
+from erpnext.hr.doctype.training_event.test_training_event import (
+ create_training_event,
+ create_training_program,
+)
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee
-from erpnext.hr.doctype.training_event.test_training_event import create_training_program, create_training_event
+
+
class TestTrainingFeedback(unittest.TestCase):
def setUp(self):
create_training_program("Basic Training")
@@ -64,4 +71,4 @@
"training_event": event,
"employee": employee,
"feedback": "Test"
- })
\ No newline at end of file
+ })
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.js b/erpnext/hr/doctype/training_feedback/training_feedback.js
index 0dea098..5e875c1 100644
--- a/erpnext/hr/doctype/training_feedback/training_feedback.js
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.js
@@ -7,4 +7,4 @@
frm.add_fetch("training_event", "event_name", "event_name");
frm.add_fetch("training_event", "trainer_name", "trainer_name");
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py
index 0d32de7..6a41a65 100644
--- a/erpnext/hr/doctype/training_feedback/training_feedback.py
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
class TrainingFeedback(Document):
def validate(self):
@@ -42,4 +44,3 @@
if employee:
frappe.db.set_value("Training Event Employee", employee, "status", "Completed")
-
diff --git a/erpnext/hr/doctype/training_program/test_training_program.js b/erpnext/hr/doctype/training_program/test_training_program.js
deleted file mode 100644
index 3a62b2f..0000000
--- a/erpnext/hr/doctype/training_program/test_training_program.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Training Program", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Training Program
- () => frappe.tests.make('Training Program', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/training_program/test_training_program.py b/erpnext/hr/doctype/training_program/test_training_program.py
index 9d5b286..aec319f 100644
--- a/erpnext/hr/doctype/training_program/test_training_program.py
+++ b/erpnext/hr/doctype/training_program/test_training_program.py
@@ -5,5 +5,6 @@
import unittest
+
class TestTrainingProgram(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/training_program/training_program.js b/erpnext/hr/doctype/training_program/training_program.js
index 7d85cab..a4ccf54 100644
--- a/erpnext/hr/doctype/training_program/training_program.js
+++ b/erpnext/hr/doctype/training_program/training_program.js
@@ -2,4 +2,4 @@
// For license information, please see license.txt
frappe.ui.form.on('Training Program', {
-});
\ No newline at end of file
+});
diff --git a/erpnext/hr/doctype/training_program/training_program.py b/erpnext/hr/doctype/training_program/training_program.py
index 7a3720b..6f3ab5a 100644
--- a/erpnext/hr/doctype/training_program/training_program.py
+++ b/erpnext/hr/doctype/training_program/training_program.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class TrainingProgram(Document):
pass
diff --git a/erpnext/hr/doctype/training_program/training_program_dashboard.py b/erpnext/hr/doctype/training_program/training_program_dashboard.py
index 441a71b..b2eed68 100644
--- a/erpnext/hr/doctype/training_program/training_program_dashboard.py
+++ b/erpnext/hr/doctype/training_program/training_program_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'training_program',
@@ -10,4 +12,4 @@
'items': ['Training Event']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/training_result/test_training_result.js b/erpnext/hr/doctype/training_result/test_training_result.js
deleted file mode 100644
index cb1d7fb..0000000
--- a/erpnext/hr/doctype/training_result/test_training_result.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Training Result", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Training Result
- () => frappe.tests.make('Training Result', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/training_result/test_training_result.py b/erpnext/hr/doctype/training_result/test_training_result.py
index 29ed2a0..17ccc4b 100644
--- a/erpnext/hr/doctype/training_result/test_training_result.py
+++ b/erpnext/hr/doctype/training_result/test_training_result.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Training Result')
diff --git a/erpnext/hr/doctype/training_result/training_result.js b/erpnext/hr/doctype/training_result/training_result.js
index 62ac383..718b383 100644
--- a/erpnext/hr/doctype/training_result/training_result.js
+++ b/erpnext/hr/doctype/training_result/training_result.js
@@ -11,7 +11,7 @@
},
training_event: function(frm) {
- if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) {
+ if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) {
frappe.call({
method: "erpnext.hr.doctype.training_result.training_result.get_employees",
args: {
@@ -21,7 +21,7 @@
frm.set_value("employees" ,"");
if (r.message) {
$.each(r.message, function(i, d) {
- var row = frappe.model.add_child(cur_frm.doc, "Training Result Employee", "employees");
+ var row = frappe.model.add_child(frm.doc, "Training Result Employee", "employees");
row.employee = d.employee;
row.employee_name = d.employee_name;
});
diff --git a/erpnext/hr/doctype/training_result/training_result.py b/erpnext/hr/doctype/training_result/training_result.py
index 7cdc51f..9cfc570 100644
--- a/erpnext/hr/doctype/training_result/training_result.py
+++ b/erpnext/hr/doctype/training_result/training_result.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
from erpnext.hr.doctype.employee.employee import get_employee_emails
+
class TrainingResult(Document):
def validate(self):
training_event = frappe.get_doc("Training Event", self.training_event)
diff --git a/erpnext/hr/doctype/training_result_employee/test_training_result.js b/erpnext/hr/doctype/training_result_employee/test_training_result.js
index 2ebf896..3f39750 100644
--- a/erpnext/hr/doctype/training_result_employee/test_training_result.js
+++ b/erpnext/hr/doctype/training_result_employee/test_training_result.js
@@ -50,4 +50,3 @@
() => done()
]);
});
-
diff --git a/erpnext/hr/doctype/training_result_employee/training_result_employee.py b/erpnext/hr/doctype/training_result_employee/training_result_employee.py
index 54e2a18..b0d4605 100644
--- a/erpnext/hr/doctype/training_result_employee/training_result_employee.py
+++ b/erpnext/hr/doctype/training_result_employee/training_result_employee.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TrainingResultEmployee(Document):
pass
diff --git a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py b/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py
index 0b369be..467ef16 100644
--- a/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py
+++ b/erpnext/hr/doctype/travel_itinerary/travel_itinerary.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TravelItinerary(Document):
pass
diff --git a/erpnext/hr/doctype/travel_request/test_travel_request.js b/erpnext/hr/doctype/travel_request/test_travel_request.js
deleted file mode 100644
index 7e64591..0000000
--- a/erpnext/hr/doctype/travel_request/test_travel_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Travel Request", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Travel Request
- () => frappe.tests.make('Travel Request', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/travel_request/test_travel_request.py b/erpnext/hr/doctype/travel_request/test_travel_request.py
index dac5517..95bf8b9 100644
--- a/erpnext/hr/doctype/travel_request/test_travel_request.py
+++ b/erpnext/hr/doctype/travel_request/test_travel_request.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestTravelRequest(unittest.TestCase):
pass
diff --git a/erpnext/hr/doctype/travel_request/travel_request.py b/erpnext/hr/doctype/travel_request/travel_request.py
index 60834d3..b10333f 100644
--- a/erpnext/hr/doctype/travel_request/travel_request.py
+++ b/erpnext/hr/doctype/travel_request/travel_request.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
from erpnext.hr.utils import validate_active_employee
+
class TravelRequest(Document):
def validate(self):
validate_active_employee(self.employee)
diff --git a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py b/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py
index 9fa85e8..9b38d88 100644
--- a/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py
+++ b/erpnext/hr/doctype/travel_request_costing/travel_request_costing.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TravelRequestCosting(Document):
pass
diff --git a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
index 03b0cf3..e0a776c 100644
--- a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py
@@ -3,12 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-import erpnext
+
+import frappe
from frappe.utils import getdate
-from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data
+
+import erpnext
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']
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
index 674c8e3..030ecec 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
@@ -4,13 +4,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cstr, add_days, date_diff, getdate
from frappe import _
-from frappe.utils.csvutils import UnicodeWriter
from frappe.model.document import Document
+from frappe.utils import add_days, cstr, date_diff, getdate
+from frappe.utils.csvutils import UnicodeWriter
+
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
-from erpnext.hr.utils import get_holidays_for_employee
+from erpnext.hr.utils import get_holiday_dates_for_employee
+
class UploadAttendance(Document):
pass
@@ -94,7 +97,7 @@
holidays = {}
for employee in employees:
holiday_list = get_holiday_list_for_employee(employee)
- holiday = get_holidays_for_employee(employee, getdate(from_date), getdate(to_date))
+ holiday = get_holiday_dates_for_employee(employee, getdate(from_date), getdate(to_date))
if holiday_list not in holidays:
holidays[holiday_list] = holiday
diff --git a/erpnext/hr/doctype/vehicle/test_vehicle.js b/erpnext/hr/doctype/vehicle/test_vehicle.js
deleted file mode 100644
index 4d40cce..0000000
--- a/erpnext/hr/doctype/vehicle/test_vehicle.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Vehicle", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Vehicle
- () => frappe.tests.make('Vehicle', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hr/doctype/vehicle/test_vehicle.py b/erpnext/hr/doctype/vehicle/test_vehicle.py
index ff3429d..2bc94c6 100644
--- a/erpnext/hr/doctype/vehicle/test_vehicle.py
+++ b/erpnext/hr/doctype/vehicle/test_vehicle.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate,flt, cstr,random_string
+
+import frappe
+from frappe.utils import random_string
+
# test_records = frappe.get_test_records('Vehicle')
class TestVehicle(unittest.TestCase):
diff --git a/erpnext/hr/doctype/vehicle/vehicle.py b/erpnext/hr/doctype/vehicle/vehicle.py
index 1df5068..2ff1904 100644
--- a/erpnext/hr/doctype/vehicle/vehicle.py
+++ b/erpnext/hr/doctype/vehicle/vehicle.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate
from frappe.model.document import Document
+from frappe.utils import getdate
+
class Vehicle(Document):
def validate(self):
diff --git a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
index 761c701..6a01bcf 100644
--- a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
+++ b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
@@ -17,4 +19,4 @@
'items': ['Delivery Trip']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
index ed52c4e..1b0bfcb 100644
--- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
+++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py
@@ -3,12 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, flt, cstr, random_string
+
+import frappe
+from frappe.utils import cstr, flt, nowdate, random_string
+
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.vehicle_log.vehicle_log import make_expense_claim
+
class TestVehicleLog(unittest.TestCase):
def setUp(self):
employee_id = frappe.db.sql("""select name from `tabEmployee` where name='testdriver@example.com'""")
@@ -115,4 +118,4 @@
vehicle_log.save()
vehicle_log.submit()
- return vehicle_log
\ No newline at end of file
+ return vehicle_log
diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js
index 6f3a0dc..14fe9a0 100644
--- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js
+++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js
@@ -24,4 +24,3 @@
});
}
});
-
diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py
index 04c94e3..73c848b 100644
--- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py
+++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py
@@ -3,11 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, cstr
-from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
+from frappe.utils import flt
+
class VehicleLog(Document):
def validate(self):
diff --git a/erpnext/hr/doctype/vehicle_service/vehicle_service.py b/erpnext/hr/doctype/vehicle_service/vehicle_service.py
index 18ed782..bc93a97 100644
--- a/erpnext/hr/doctype/vehicle_service/vehicle_service.py
+++ b/erpnext/hr/doctype/vehicle_service/vehicle_service.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class VehicleService(Document):
pass
diff --git a/erpnext/hr/module_onboarding/human_resource/human_resource.json b/erpnext/hr/module_onboarding/human_resource/human_resource.json
index 518c002..cd11bd1 100644
--- a/erpnext/hr/module_onboarding/human_resource/human_resource.json
+++ b/erpnext/hr/module_onboarding/human_resource/human_resource.json
@@ -13,17 +13,14 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources",
"idx": 0,
"is_complete": 0,
- "modified": "2020-07-08 14:05:47.018799",
+ "modified": "2021-05-19 05:32:01.794628",
"modified_by": "Administrator",
"module": "HR",
"name": "Human Resource",
"owner": "Administrator",
"steps": [
{
- "step": "Create Department"
- },
- {
- "step": "Create Designation"
+ "step": "HR Settings"
},
{
"step": "Create Holiday list"
@@ -32,6 +29,9 @@
"step": "Create Employee"
},
{
+ "step": "Data import"
+ },
+ {
"step": "Create Leave Type"
},
{
@@ -39,9 +39,6 @@
},
{
"step": "Create Leave Application"
- },
- {
- "step": "HR Settings"
}
],
"subtitle": "Employee, Leaves, and more.",
diff --git a/erpnext/hr/notification/training_feedback/training_feedback.html b/erpnext/hr/notification/training_feedback/training_feedback.html
index fd8fef9..b49662a 100644
--- a/erpnext/hr/notification/training_feedback/training_feedback.html
+++ b/erpnext/hr/notification/training_feedback/training_feedback.html
@@ -3,4 +3,4 @@
<p>You attended training {{ frappe.utils.get_link_to_form(
"Training Event", doc.training_event) }}</p>
-<p>{{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}</p>
\ No newline at end of file
+<p>{{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}</p>
diff --git a/erpnext/hr/notification/training_feedback/training_feedback.py b/erpnext/hr/notification/training_feedback/training_feedback.py
index 2334f8b..f57de91 100644
--- a/erpnext/hr/notification/training_feedback/training_feedback.py
+++ b/erpnext/hr/notification/training_feedback/training_feedback.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.html b/erpnext/hr/notification/training_scheduled/training_scheduled.html
index 374038a..50f6d07 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.html
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.html
@@ -41,4 +41,4 @@
<td width="15"></td>
</tr>
<tr height="10"></tr>
-</table>
\ No newline at end of file
+</table>
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.py b/erpnext/hr/notification/training_scheduled/training_scheduled.py
index 2334f8b..f57de91 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.py
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/hr/onboarding_step/create_employee/create_employee.json b/erpnext/hr/onboarding_step/create_employee/create_employee.json
index 3aa33c6..4782818 100644
--- a/erpnext/hr/onboarding_step/create_employee/create_employee.json
+++ b/erpnext/hr/onboarding_step/create_employee/create_employee.json
@@ -1,18 +1,20 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Show Tour",
"creation": "2020-05-14 11:43:25.561152",
+ "description": "<h3>Employee</h3>\n\nAn individual who works and is recognized for his rights and duties in your company is your Employee. You can manage the Employee master. It captures the demographic, personal and professional details, joining and leave details, etc.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-14 12:26:28.629074",
+ "modified": "2021-05-19 04:50:02.240321",
"modified_by": "Administrator",
"name": "Create Employee",
"owner": "Administrator",
"reference_document": "Employee",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create Employee",
"validate_action": 0
diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json
index 32472b4..a08e85f 100644
--- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json
+++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json
@@ -1,18 +1,20 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Show Tour",
"creation": "2020-05-28 11:47:34.700174",
+ "description": "<h3>Holiday List.</h3>\n\nHoliday List is a list which contains the dates of holidays. Most organizations have a standard Holiday List for their employees. However, some of them may have different holiday lists based on different Locations or Departments. In ERPNext, you can configure multiple Holiday Lists.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-14 12:25:38.068582",
+ "modified": "2021-05-19 04:19:52.305199",
"modified_by": "Administrator",
"name": "Create Holiday list",
"owner": "Administrator",
"reference_document": "Holiday List",
+ "show_form_tour": 0,
"show_full_form": 1,
"title": "Create Holiday List",
"validate_action": 0
diff --git a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json
index fa9941e..0b0ce3f 100644
--- a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json
+++ b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json
@@ -1,18 +1,20 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Show Tour",
"creation": "2020-05-14 11:48:56.123718",
+ "description": "<h3>Leave Allocation</h3>\n\nLeave Allocation enables you to allocate a specific number of leaves of a particular type to an Employee so that, an employee will be able to create a Leave Application only if Leaves are allocated. ",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-14 11:48:56.123718",
+ "modified": "2021-05-19 04:22:34.220238",
"modified_by": "Administrator",
"name": "Create Leave Allocation",
"owner": "Administrator",
"reference_document": "Leave Allocation",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create Leave Allocation",
"validate_action": 0
diff --git a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json
index 1ed074e..af63aa5 100644
--- a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json
+++ b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json
@@ -1,18 +1,20 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Show Tour",
"creation": "2020-05-14 11:49:45.400764",
+ "description": "<h3>Leave Application</h3>\n\nLeave Application is a formal document created by an Employee to apply for Leaves for a particular time period based on there leave allocation and leave type according to there need.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-14 11:49:45.400764",
+ "modified": "2021-05-19 04:39:09.893474",
"modified_by": "Administrator",
"name": "Create Leave Application",
"owner": "Administrator",
"reference_document": "Leave Application",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create Leave Application",
"validate_action": 0
diff --git a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json
index 8cbfc5c..397f5cd 100644
--- a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json
+++ b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json
@@ -1,18 +1,20 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Show Tour",
"creation": "2020-05-27 11:17:31.119312",
+ "description": "<h3>Leave Type</h3>\n\nLeave type is defined based on many factors and features like encashment, earned leaves, partially paid, without pay and, a lot more. To check other options and to define your leave type click on Show Tour.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-20 11:17:31.119312",
+ "modified": "2021-05-19 04:32:48.135406",
"modified_by": "Administrator",
"name": "Create Leave Type",
"owner": "Administrator",
"reference_document": "Leave Type",
+ "show_form_tour": 0,
"show_full_form": 1,
"title": "Create Leave Type",
"validate_action": 0
diff --git a/erpnext/hr/onboarding_step/data_import/data_import.json b/erpnext/hr/onboarding_step/data_import/data_import.json
new file mode 100644
index 0000000..ac343c6
--- /dev/null
+++ b/erpnext/hr/onboarding_step/data_import/data_import.json
@@ -0,0 +1,21 @@
+{
+ "action": "Watch Video",
+ "action_label": "",
+ "creation": "2021-05-19 05:29:16.809610",
+ "description": "<h3>Data Import</h3>\n\nData import is the tool to migrate your existing data like Employee, Customer, Supplier, and a lot more to our ERPNext system.\nGo through the video for a detailed explanation of this tool.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-05-19 05:29:16.809610",
+ "modified_by": "Administrator",
+ "name": "Data import",
+ "owner": "Administrator",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Data Import",
+ "validate_action": 1,
+ "video_url": "https://www.youtube.com/watch?v=DQyqeurPI64"
+}
\ No newline at end of file
diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json
index 0a1d0ba..355664f 100644
--- a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json
+++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json
@@ -1,18 +1,20 @@
{
- "action": "Update Settings",
+ "action": "Show Form Tour",
+ "action_label": "Explore",
"creation": "2020-05-28 13:13:52.427711",
+ "description": "<h3>HR Settings</h3>\n\nHr Settings consists of major settings related to Employee Lifecycle, Leave Management, etc. Click on Explore, to explore Hr Settings.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
- "modified": "2020-05-20 11:16:42.430974",
+ "modified": "2021-05-18 07:02:05.747548",
"modified_by": "Administrator",
"name": "HR Settings",
"owner": "Administrator",
"reference_document": "HR Settings",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "HR Settings",
"validate_action": 0
diff --git a/erpnext/healthcare/doctype/body_part/__init__.py b/erpnext/hr/page/organizational_chart/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/body_part/__init__.py
rename to erpnext/hr/page/organizational_chart/__init__.py
diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js
new file mode 100644
index 0000000..b0e41e0
--- /dev/null
+++ b/erpnext/hr/page/organizational_chart/organizational_chart.js
@@ -0,0 +1,23 @@
+frappe.pages['organizational-chart'].on_page_load = function(wrapper) {
+ frappe.ui.make_app_page({
+ parent: wrapper,
+ title: __('Organizational Chart'),
+ single_column: true
+ });
+
+ $(wrapper).bind('show', () => {
+ frappe.require('hierarchy-chart.bundle.js', () => {
+ let organizational_chart = undefined;
+ let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children';
+
+ if (frappe.is_mobile()) {
+ organizational_chart = new erpnext.HierarchyChartMobile('Employee', wrapper, method);
+ } else {
+ organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method);
+ }
+
+ frappe.breadcrumbs.add('HR');
+ organizational_chart.show();
+ });
+ });
+};
diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.json b/erpnext/hr/page/organizational_chart/organizational_chart.json
new file mode 100644
index 0000000..d802781
--- /dev/null
+++ b/erpnext/hr/page/organizational_chart/organizational_chart.json
@@ -0,0 +1,26 @@
+{
+ "content": null,
+ "creation": "2021-05-25 10:53:10.107241",
+ "docstatus": 0,
+ "doctype": "Page",
+ "idx": 0,
+ "modified": "2021-05-25 10:53:18.201931",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "organizational-chart",
+ "owner": "Administrator",
+ "page_name": "Organizational Chart",
+ "roles": [
+ {
+ "role": "HR User"
+ },
+ {
+ "role": "HR Manager"
+ }
+ ],
+ "script": null,
+ "standard": "Yes",
+ "style": null,
+ "system_page": 0,
+ "title": "Organizational Chart"
+}
\ No newline at end of file
diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py
new file mode 100644
index 0000000..1baf805
--- /dev/null
+++ b/erpnext/hr/page/organizational_chart/organizational_chart.py
@@ -0,0 +1,50 @@
+from __future__ import unicode_literals
+
+import frappe
+
+
+@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])
+
+ if parent and company and parent != company:
+ filters.append(['reports_to', '=', parent])
+ else:
+ filters.append(['reports_to', '=', ''])
+
+ if 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'],
+ filters=filters,
+ order_by='name'
+ )
+
+ for employee in employees:
+ 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
+
+ return employees
+
+
+def get_connections(employee):
+ num_connections = 0
+
+ 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]
+ ])
+ num_connections += len(descendants)
+ nodes_to_expand.extend(descendants)
+
+ return num_connections
diff --git a/erpnext/hr/page/team_updates/team_updates.py b/erpnext/hr/page/team_updates/team_updates.py
index a6cf935..a5e7c44 100644
--- a/erpnext/hr/page/team_updates/team_updates.py
+++ b/erpnext/hr/page/team_updates/team_updates.py
@@ -3,6 +3,7 @@
import frappe
from email_reply_parser import EmailReplyParser
+
@frappe.whitelist()
def get_data(start=0):
#frappe.only_for('Employee', 'System Manager')
@@ -17,4 +18,4 @@
if d.text_content:
d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content))
- return data
\ No newline at end of file
+ return data
diff --git a/erpnext/hr/print_format/job_offer/job_offer.json b/erpnext/hr/print_format/job_offer/job_offer.json
index 3fc2bcf..0a92230 100644
--- a/erpnext/hr/print_format/job_offer/job_offer.json
+++ b/erpnext/hr/print_format/job_offer/job_offer.json
@@ -15,7 +15,7 @@
"name": "Job Offer",
"owner": "Administrator",
"print_format_builder": 0,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html
index d60582e..87daafc 100644
--- a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html
+++ b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html
@@ -35,4 +35,4 @@
<div>
<span><b>________________</b></span><br>
<span><b>{{ doc.applicant_name }}</b></span>
-</div>
\ No newline at end of file
+</div>
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 aa8eea5..62ffb7d 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
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe import _
+
import frappe
+from frappe import _
+
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group
+
def execute(filters=None):
if not filters.group: return [], []
columns, data = get_columns(), get_data(filters)
@@ -54,4 +57,4 @@
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
\ No newline at end of file
+ return data
diff --git a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js
index 528ae4c..8de4af5 100644
--- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js
+++ b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js
@@ -38,4 +38,3 @@
}
]
};
-
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 363e31d..d0c295d 100644
--- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
+++ b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
+from frappe import _, msgprint
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py
index 8f39388..725c5a1 100644
--- a/erpnext/hr/report/employee_analytics/employee_analytics.py
+++ b/erpnext/hr/report/employee_analytics/employee_analytics.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if not filters: filters = {}
@@ -81,4 +83,3 @@
}
chart["type"] = "donut"
return chart
-
diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.js b/erpnext/hr/report/employee_birthday/employee_birthday.js
index 60b69b4..bbe4a8d 100644
--- a/erpnext/hr/report/employee_birthday/employee_birthday.js
+++ b/erpnext/hr/report/employee_birthday/employee_birthday.js
@@ -8,7 +8,7 @@
"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",
+ "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
"Dec"][frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth()],
},
{
@@ -19,4 +19,4 @@
"default": frappe.defaults.get_user_default("Company")
}
]
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.py b/erpnext/hr/report/employee_birthday/employee_birthday.py
index e8d7844..b284e6b 100644
--- a/erpnext/hr/report/employee_birthday/employee_birthday.py
+++ b/erpnext/hr/report/employee_birthday/employee_birthday.py
@@ -2,9 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
+
def execute(filters=None):
if not filters: filters = {}
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 b8953b3..6bca136 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -2,12 +2,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt, add_days
-from frappe import _
-from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on
+
from itertools import groupby
+import frappe
+from frappe import _
+from frappe.utils import add_days
+
+from erpnext.hr.doctype.leave_application.leave_application import (
+ get_leave_balance_on,
+ get_leaves_for_period,
+)
+
+
def execute(filters=None):
if filters.to_date <= filters.from_date:
frappe.throw(_('"From Date" can not be greater than or equal to "To Date"'))
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 e86fa2b..bcbb066 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
@@ -2,14 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
-from erpnext.hr.doctype.leave_application.leave_application \
- import get_leave_details
-from erpnext.hr.report.employee_leave_balance.employee_leave_balance \
- import get_department_leave_approver_map
+from erpnext.hr.doctype.leave_application.leave_application import get_leave_details
+from erpnext.hr.report.employee_leave_balance.employee_leave_balance import (
+ get_department_leave_approver_map,
+)
+
def execute(filters=None):
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
@@ -65,7 +66,7 @@
for leave_type in leave_types:
remaining = 0
if leave_type in available_leave["leave_allocation"]:
- # opening balance
+ # opening balance
remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves']
row += [remaining]
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 59f56d7..3a26882 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
@@ -2,6 +2,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
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 bcb0ee4..c6e6432 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import cstr, cint, getdate
-from frappe import msgprint, _
+
from calendar import monthrange
+import frappe
+from frappe import _, msgprint
+from frappe.utils import cint, cstr, getdate
+
status_map = {
"Absent": "A",
"Half Day": "HD",
diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js
index 9620f52..51dc7ff 100644
--- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js
+++ b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js
@@ -20,4 +20,4 @@
"reqd": 1,
},
]
-};
\ No newline at end of file
+};
diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
index 303c829..c598e9e 100644
--- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
+++ b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
index 26e0f26..2ba87ef 100644
--- a/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
@@ -2,14 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
from frappe.utils import getdate
-from erpnext.hr.doctype.employee.test_employee import make_employee
-from erpnext.hr.doctype.vehicle_log.vehicle_log import make_expense_claim
-from erpnext.hr.doctype.vehicle_log.test_vehicle_log import get_vehicle, make_vehicle_log
-from erpnext.hr.report.vehicle_expenses.vehicle_expenses import execute
+
from erpnext.accounts.utils import get_fiscal_year
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.vehicle_log.test_vehicle_log import get_vehicle, make_vehicle_log
+from erpnext.hr.doctype.vehicle_log.vehicle_log import make_expense_claim
+from erpnext.hr.report.vehicle_expenses.vehicle_expenses import execute
+
class TestVehicleExpenses(unittest.TestCase):
@classmethod
diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js
index 879acd1..2d0aa0f 100644
--- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js
+++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js
@@ -49,4 +49,3 @@
}
]
};
-
diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
index d847cbb..2be3565 100644
--- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
@@ -2,12 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _
from frappe.utils import flt
+
from erpnext.accounts.report.financial_statements import get_period_list
+
def execute(filters=None):
filters = frappe._dict(filters or {})
@@ -96,8 +98,6 @@
}
]
- return columns
-
def get_vehicle_log_data(filters):
start_date, end_date = get_period_dates(filters)
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index 992b18d..b6f4cad 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -1,14 +1,27 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import erpnext
import frappe
-from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, InactiveEmployeeStatusError
from frappe import _
-from frappe.desk.form import assign_to
-from frappe.model.document import Document
-from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate,
- get_datetime, getdate, nowdate, today, unique, get_link_to_form)
+from frappe.utils import (
+ add_days,
+ cstr,
+ flt,
+ format_datetime,
+ formatdate,
+ get_datetime,
+ get_link_to_form,
+ getdate,
+ nowdate,
+ today,
+)
+
+import erpnext
+from erpnext.hr.doctype.employee.employee import (
+ InactiveEmployeeStatusError,
+ get_holiday_list_for_employee,
+)
+
class DuplicateDeclarationError(frappe.ValidationError): pass
@@ -19,7 +32,10 @@
def update_employee(employee, details, date=None, cancel=False):
internal_work_history = {}
for item in details:
- fieldtype = frappe.get_meta("Employee").get_field(item.fieldname).fieldtype
+ field = frappe.get_meta("Employee").get_field(item.fieldname)
+ if not field:
+ continue
+ fieldtype = field.fieldtype
new_data = item.new if not cancel else item.current
if fieldtype == "Date" and new_data:
new_data = getdate(new_data)
@@ -272,6 +288,7 @@
def check_effective_date(from_date, to_date, frequency, based_on_date_of_joining_date):
import calendar
+
from dateutil import relativedelta
from_date = get_datetime(from_date)
@@ -335,20 +352,43 @@
total_given_benefit_amount = sum_of_given_benefit[0].total_amount
return total_given_benefit_amount
-def get_holidays_for_employee(employee, start_date, end_date):
- holiday_list = get_holiday_list_for_employee(employee)
+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
+ holidays = get_holidays_for_employee(employee, start_date, end_date)
- holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
- where
- parent=%(holiday_list)s
- and holiday_date >= %(start_date)s
- and holiday_date <= %(end_date)s''', {
- "holiday_list": holiday_list,
- "start_date": start_date,
- "end_date": end_date
- })
+ return [cstr(h.holiday_date) for h in holidays]
- holidays = [cstr(i) for i in holidays]
+
+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)
+
+ 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])
+ }
+
+ if only_non_weekly:
+ filters['weekly_off'] = False
+
+ holidays = frappe.get_all(
+ 'Holiday',
+ fields=['description', 'holiday_date'],
+ filters=filters
+ )
return holidays
@@ -413,4 +453,4 @@
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)
\ No newline at end of file
+ get_link_to_form("Employee", employee)), InactiveEmployeeStatusError)
diff --git a/erpnext/hr/web_form/job_application/job_application.js b/erpnext/hr/web_form/job_application/job_application.js
index 699703c..ffc5e98 100644
--- a/erpnext/hr/web_form/job_application/job_application.js
+++ b/erpnext/hr/web_form/job_application/job_application.js
@@ -1,3 +1,3 @@
frappe.ready(function() {
// bind events here
-})
\ No newline at end of file
+})
diff --git a/erpnext/hr/web_form/job_application/job_application.py b/erpnext/hr/web_form/job_application/job_application.py
index 2334f8b..f57de91 100644
--- a/erpnext/hr/web_form/job_application/job_application.py
+++ b/erpnext/hr/web_form/job_application/job_application.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index 575fa7b..9c5d0c1 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -224,6 +224,17 @@
"type": "Link"
},
{
+ "dependencies": "Employee",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Full and Final Statement",
+ "link_count": 0,
+ "link_to": "Full and Final Statement",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
"hidden": 0,
"is_query_report": 0,
"label": "Shift Management",
@@ -931,7 +942,7 @@
"type": "Link"
}
],
- "modified": "2021-08-05 12:15:59.842918",
+ "modified": "2021-08-31 12:18:59.842918",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py
index 85ffe29..6ac3255 100644
--- a/erpnext/hub_node/__init__.py
+++ b/erpnext/hub_node/__init__.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+
@frappe.whitelist()
def enable_hub():
hub_settings = frappe.get_doc('Marketplace Settings')
diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py
index 42f9000..5530491 100644
--- a/erpnext/hub_node/api.py
+++ b/erpnext/hub_node/api.py
@@ -1,11 +1,11 @@
from __future__ import unicode_literals
-import frappe
import json
+import frappe
from frappe import _
-from frappe.frappeclient import FrappeClient
from frappe.desk.form.load import get_attachments
+from frappe.frappeclient import FrappeClient
from six import string_types
current_user = frappe.session.user
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py
index be2cd6b..823c79e 100644
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py
+++ b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HubTrackedItem(Document):
pass
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
index 92b2940..c403f90 100644
--- a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
+++ b/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestHubTrackedItem(unittest.TestCase):
pass
diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.py b/erpnext/hub_node/doctype/hub_user/hub_user.py
index de43f4e..1f7c8fc 100644
--- a/erpnext/hub_node/doctype/hub_user/hub_user.py
+++ b/erpnext/hub_node/doctype/hub_user/hub_user.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HubUser(Document):
pass
diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.py b/erpnext/hub_node/doctype/hub_users/hub_users.py
index 440be14..e08ed68 100644
--- a/erpnext/hub_node/doctype/hub_users/hub_users.py
+++ b/erpnext/hub_node/doctype/hub_users/hub_users.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HubUsers(Document):
pass
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
index 91c7bf5..33d23f6 100644
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
+++ b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
@@ -2,14 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, requests, json, time
-from frappe.model.document import Document
-from frappe.utils import add_years, now, get_datetime, get_datetime_str, cint
-from frappe import _
+import json
+
+import frappe
from frappe.frappeclient import FrappeClient
-from erpnext.utilities.product import get_price, get_qty_in_stock
-from six import string_types
+from frappe.model.document import Document
+from frappe.utils import cint
+
class MarketplaceSettings(Document):
@@ -83,7 +83,6 @@
def unregister(self):
"""Disable the User on hubmarket.org"""
- pass
@frappe.whitelist()
def is_marketplace_enabled():
diff --git a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.js b/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.js
deleted file mode 100644
index fba3e09..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Marketplace Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Marketplace Settings
- () => frappe.tests.make('Marketplace Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py
index 549b991..7922f45 100644
--- a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py
+++ b/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestMarketplaceSettings(unittest.TestCase):
pass
diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py
index b61b88b..2e4c266 100644
--- a/erpnext/hub_node/legacy.py
+++ b/erpnext/hub_node/legacy.py
@@ -1,9 +1,13 @@
from __future__ import unicode_literals
-import frappe, json
-from frappe.utils import nowdate
-from frappe.frappeclient import FrappeClient
-from frappe.utils.nestedset import get_root_of
+
+import json
+
+import frappe
from frappe.contacts.doctype.contact.contact import get_default_contact
+from frappe.frappeclient import FrappeClient
+from frappe.utils import nowdate
+from frappe.utils.nestedset import get_root_of
+
def get_list(doctype, start, limit, fields, filters, order_by):
pass
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
index cf75cc8..5817941 100644
--- a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
@@ -11,4 +11,4 @@
default: frappe.defaults.get_user_default("Company")
}
]
-};
\ No newline at end of file
+};
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 6bb0440..0911e8f 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
@@ -2,12 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils.dashboard import cache_source
-from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
- import get_loan_security_details
from six import iteritems
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure import (
+ get_loan_security_details,
+)
+
+
@frappe.whitelist()
@cache_source
def get_data(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
@@ -73,4 +77,4 @@
'chartType': 'bar',
'values': values
}]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index ff7fbbd..7dbd422 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -3,14 +3,22 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, math, json
-import erpnext
+
+import json
+import math
+
+import frappe
from frappe import _
+from frappe.utils import add_months, flt, getdate, now_datetime, nowdate
from six import string_types
-from frappe.utils import flt, rounded, add_months, nowdate, getdate, now_datetime
-from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
+
+import erpnext
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
+from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import (
+ get_pledged_security_qty,
+)
+
class Loan(AccountsController):
def validate(self):
@@ -361,7 +369,9 @@
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
+ 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:
diff --git a/erpnext/loan_management/doctype/loan/loan_dashboard.py b/erpnext/loan_management/doctype/loan/loan_dashboard.py
index 7a8190f..28ccc03 100644
--- a/erpnext/loan_management/doctype/loan/loan_dashboard.py
+++ b/erpnext/loan_management/doctype/loan/loan_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -16,4 +16,4 @@
'items': ['Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off', 'Loan Security Unpledge']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 314f58d..ec0aebb 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -3,23 +3,40 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import erpnext
import unittest
-from frappe.utils import (nowdate, add_days, getdate, now_datetime, add_to_date, get_datetime,
- add_months, get_first_day, get_last_day, flt, date_diff)
-from erpnext.selling.doctype.customer.test_customer import get_customer_dict
-from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (process_loan_interest_accrual_for_demand_loans,
- process_loan_interest_accrual_for_term_loans)
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
-from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall
-from erpnext.loan_management.doctype.loan.loan import unpledge_security, request_loan_closure, make_loan_write_off
-from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
+
+import frappe
+from frappe.utils import add_days, add_months, add_to_date, date_diff, flt, get_datetime, nowdate
+
+from erpnext.loan_management.doctype.loan.loan import (
+ make_loan_write_off,
+ request_loan_closure,
+ unpledge_security,
+)
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
-from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount
+from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import (
+ get_disbursal_amount,
+)
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (
+ days_in_year,
+)
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
-from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import (
+ get_pledged_security_qty,
+)
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+ process_loan_interest_accrual_for_demand_loans,
+ process_loan_interest_accrual_for_term_loans,
+)
+from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import (
+ create_process_loan_security_shortfall,
+)
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+ make_employee,
+ make_salary_structure,
+)
+from erpnext.selling.doctype.customer.test_customer import get_customer_dict
+
class TestLoan(unittest.TestCase):
def setUp(self):
@@ -988,4 +1005,4 @@
loan.save()
- return loan
\ No newline at end of file
+ return loan
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index d8f3577..e492920 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -3,17 +3,28 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, math
-from frappe import _
-from frappe.utils import flt, rounded, cint
-from frappe.model.mapper import get_mapped_doc
-from frappe.model.document import Document
-from erpnext.loan_management.doctype.loan.loan import (get_monthly_repayment_amount, validate_repayment_method,
- get_total_loan_amount, get_sanctioned_amount_limit)
-from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
+
import json
+import math
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, flt, rounded
from six import string_types
+from erpnext.loan_management.doctype.loan.loan import (
+ get_monthly_repayment_amount,
+ get_sanctioned_amount_limit,
+ get_total_loan_amount,
+ validate_repayment_method,
+)
+from erpnext.loan_management.doctype.loan_security_price.loan_security_price import (
+ get_loan_security_price,
+)
+
+
class LoanApplication(Document):
def validate(self):
self.set_pledge_amount()
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 bf3f58b..992d669 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Loan', 'Loan Security Pledge']
},
],
- }
\ No newline at end of file
+ }
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 2a659e9..aefa089 100644
--- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py
@@ -3,10 +3,16 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee, make_salary_structure
-from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan_accounts
+
+import frappe
+
+from erpnext.loan_management.doctype.loan.test_loan import create_loan_accounts, create_loan_type
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+ make_employee,
+ make_salary_structure,
+)
+
class TestLoanApplication(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index f341e81..6d9d4f4 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -3,15 +3,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.model.document import Document
-from frappe.utils import nowdate, getdate, add_days, flt
-from erpnext.controllers.accounts_controller import AccountsController
+from frappe.utils import add_days, flt, get_datetime, nowdate
+
+import erpnext
from erpnext.accounts.general_ledger import make_gl_entries
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
-from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
-from frappe.utils import get_datetime
+from erpnext.controllers.accounts_controller import AccountsController
+from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import (
+ get_pledged_security_qty,
+)
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+ process_loan_interest_accrual_for_demand_loans,
+)
+
class LoanDisbursement(AccountsController):
@@ -203,5 +209,3 @@
disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
return disbursal_amount
-
-
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 da56710..b17c9a1 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -2,16 +2,44 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
-from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date)
-from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, create_loan_application,
- make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price)
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year, get_per_day_interest
-from erpnext.selling.doctype.customer.test_customer import get_customer_dict
+
+import frappe
+from frappe.utils import (
+ add_days,
+ add_to_date,
+ date_diff,
+ flt,
+ get_datetime,
+ get_first_day,
+ get_last_day,
+ nowdate,
+)
+
+from erpnext.loan_management.doctype.loan.test_loan import (
+ create_demand_loan,
+ create_loan_accounts,
+ create_loan_application,
+ create_loan_security,
+ create_loan_security_pledge,
+ create_loan_security_price,
+ create_loan_security_type,
+ create_loan_type,
+ create_repayment_entry,
+ make_loan_disbursement_entry,
+)
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (
+ days_in_year,
+ get_per_day_interest,
+)
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+ process_loan_interest_accrual_for_demand_loans,
+)
+from erpnext.selling.doctype.customer.test_customer import get_customer_dict
+
class TestLoanDisbursement(unittest.TestCase):
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 7978350..93513a8 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
@@ -3,13 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.model.document import Document
-from frappe.utils import (nowdate, getdate, now_datetime, get_datetime, flt, date_diff, get_last_day, cint,
- get_first_day, get_datetime, add_days)
-from erpnext.controllers.accounts_controller import AccountsController
+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
+
class LoanInterestAccrual(AccountsController):
def validate(self):
@@ -247,4 +249,3 @@
posting_date = getdate()
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 eb626f3..06b801e 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
@@ -2,15 +2,31 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
-from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date)
-from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_price,
- make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_application)
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
-from erpnext.selling.doctype.customer.test_customer import get_customer_dict
+
+import frappe
+from frappe.utils import add_to_date, date_diff, flt, get_datetime, get_first_day, nowdate
+
+from erpnext.loan_management.doctype.loan.test_loan import (
+ create_demand_loan,
+ create_loan_accounts,
+ create_loan_application,
+ create_loan_security,
+ create_loan_security_price,
+ create_loan_security_type,
+ create_loan_type,
+ make_loan_disbursement_entry,
+)
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (
+ days_in_year,
+)
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+ process_loan_interest_accrual_for_demand_loans,
+)
+from erpnext.selling.doctype.customer.test_customer import get_customer_dict
+
class TestLoanInterestAccrual(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index b8b1a40..13b7357 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -3,18 +3,26 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-import json
+
+import frappe
from frappe import _
-from frappe.utils import flt, getdate, cint
+from frappe.utils import add_days, cint, date_diff, flt, get_datetime, getdate
from six import iteritems
-from frappe.model.document import Document
-from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime
-from erpnext.controllers.accounts_controller import AccountsController
+
+import erpnext
from erpnext.accounts.general_ledger import make_gl_entries
-from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import update_shortfall_status
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import get_per_day_interest, get_last_accrual_date
+from erpnext.controllers.accounts_controller import AccountsController
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (
+ get_last_accrual_date,
+ get_per_day_interest,
+)
+from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import (
+ update_shortfall_status,
+)
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+ process_loan_interest_accrual_for_demand_loans,
+)
+
class LoanRepayment(AccountsController):
@@ -107,12 +115,13 @@
lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
- 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'
- })
+ 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'
+ })
def update_paid_amount(self):
loan = frappe.get_doc("Loan", self.against_loan)
@@ -455,6 +464,3 @@
amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount']
return amounts
-
-
-
diff --git a/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py
index 73585a5..c6ca630 100644
--- a/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/test_loan_repayment.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanRepayment(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py
index a83b9b5..495466c 100644
--- a/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py
+++ b/erpnext/loan_management/doctype/loan_repayment_detail/loan_repayment_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class LoanRepaymentDetail(Document):
pass
diff --git a/erpnext/loan_management/doctype/loan_security/loan_security.py b/erpnext/loan_management/doctype/loan_security/loan_security.py
index 8858c81..91cce67 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security.py
+++ b/erpnext/loan_management/doctype/loan_security/loan_security.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class LoanSecurity(Document):
def autoname(self):
self.name = self.loan_security_name
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 878b3fd..35f3ba6 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -12,4 +12,4 @@
'items': ['Loan Security Pledge', 'Loan Security Unpledge']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/loan_security/test_loan_security.py b/erpnext/loan_management/doctype/loan_security/test_loan_security.py
index 24dbc68..910b658 100644
--- a/erpnext/loan_management/doctype/loan_security/test_loan_security.py
+++ b/erpnext/loan_management/doctype/loan_security/test_loan_security.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanSecurity(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js
index 11c932f..48ca392 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js
@@ -40,4 +40,4 @@
qty: function(frm, cdt, cdn) {
frm.events.calculate_amounts(frm, cdt, cdn);
},
-});
\ No newline at end of file
+});
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 c390b6c..eb6c79e 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
@@ -3,12 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import now_datetime, cint
from frappe.model.document import Document
-from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import update_shortfall_status
-from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
+from frappe.utils import cint, now_datetime
+
+from erpnext.loan_management.doctype.loan_security_price.loan_security_price import (
+ get_loan_security_price,
+)
+from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import (
+ update_shortfall_status,
+)
+
class LoanSecurityPledge(Document):
def validate(self):
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py
index d2347c0..41bc78e 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanSecurityPledge(unittest.TestCase):
pass
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 32d81af..6ede6a2 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
@@ -3,11 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import getdate, now_datetime, add_to_date, get_datetime, get_timestamp, get_datetime_str
-from six import iteritems
+from frappe.utils import get_datetime
+
class LoanSecurityPrice(Document):
def validate(self):
@@ -40,12 +41,3 @@
frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(loan_security)))
else:
return loan_security_price
-
-
-
-
-
-
-
-
-
diff --git a/erpnext/loan_management/doctype/loan_security_price/test_loan_security_price.py b/erpnext/loan_management/doctype/loan_security_price/test_loan_security_price.py
index 2fe0bd5..ac63086 100644
--- a/erpnext/loan_management/doctype/loan_security_price/test_loan_security_price.py
+++ b/erpnext/loan_management/doctype/loan_security_price/test_loan_security_price.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanSecurityPrice(unittest.TestCase):
pass
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 8233b7b..5863c03 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
@@ -3,11 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import get_datetime, flt
from frappe.model.document import Document
-from six import iteritems
-from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
+from frappe.utils import flt, get_datetime
+
+from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import (
+ get_pledged_security_qty,
+)
+
class LoanSecurityShortfall(Document):
pass
@@ -71,7 +75,7 @@
- flt(loan.total_principal_paid)
pledged_securities = get_pledged_security_qty(loan.name)
- ltv_ratio = ''
+ ltv_ratio = 0.0
security_value = 0.0
for security, qty in pledged_securities.items():
@@ -122,4 +126,3 @@
"shortfall_amount": 0,
"shortfall_percentage": 0
})
-
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/test_loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/test_loan_security_shortfall.py
index b82f3d2..fefec43 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/test_loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/test_loan_security_shortfall.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanSecurityShortfall(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.py b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.py
index cb8a50a..ca1957f 100644
--- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.py
+++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class LoanSecurityType(Document):
pass
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 ac33589..7a2732e 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,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -12,4 +12,4 @@
'items': ['Loan Security Pledge', 'Loan Security Unpledge']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/loan_security_type/test_loan_security_type.py b/erpnext/loan_management/doctype/loan_security_type/test_loan_security_type.py
index f7d845a..99d7aaf 100644
--- a/erpnext/loan_management/doctype/loan_security_type/test_loan_security_type.py
+++ b/erpnext/loan_management/doctype/loan_security_type/test_loan_security_type.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanSecurityType(unittest.TestCase):
pass
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 b24dc2f..0af0de1 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
@@ -3,13 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import get_datetime, flt, getdate
-import json
+from frappe.utils import flt, get_datetime, getdate
from six import iteritems
-from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
+
class LoanSecurityUnpledge(Document):
def validate(self):
@@ -30,7 +30,9 @@
d.idx, frappe.bold(d.loan_security)))
def validate_unpledge_qty(self):
- from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import get_ltv_ratio
+ from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import (
+ get_ltv_ratio,
+ )
pledge_qty_map = get_pledged_security_qty(self.loan)
@@ -147,8 +149,3 @@
current_pledges[security] -= unpledges.get(security, 0.0)
return current_pledges
-
-
-
-
-
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py
index 5b5c205..17eb7c6 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanSecurityUnpledge(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.py b/erpnext/loan_management/doctype/loan_type/loan_type.py
index 208cb19..5458d35 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.py
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class LoanType(Document):
def validate(self):
self.validate_accounts()
@@ -21,4 +23,3 @@
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 58c6689..96b2c4a 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
+++ b/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -12,4 +12,4 @@
'items': ['Loan Application']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/loan_type/test_loan_type.py b/erpnext/loan_management/doctype/loan_type/test_loan_type.py
index 5877ab6..9e57fde 100644
--- a/erpnext/loan_management/doctype/loan_type/test_loan_type.py
+++ b/erpnext/loan_management/doctype/loan_type/test_loan_type.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanType(unittest.TestCase):
pass
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 54a3f2c..4d5e7df 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
@@ -3,11 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import getdate, flt, cint
-from erpnext.controllers.accounts_controller import AccountsController
+from frappe.utils import cint, flt, getdate
+
+import erpnext
from erpnext.accounts.general_ledger import make_gl_entries
+from erpnext.controllers.accounts_controller import AccountsController
+
class LoanWriteOff(AccountsController):
def validate(self):
@@ -84,5 +88,3 @@
)
make_gl_entries(gl_entries, cancel=cancel, merge_entries=False)
-
-
diff --git a/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py
index 9f6700e..57337c7 100644
--- a/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py
+++ b/erpnext/loan_management/doctype/loan_write_off/test_loan_write_off.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLoanWriteOff(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/pledge/pledge.py b/erpnext/loan_management/doctype/pledge/pledge.py
index 0457ad7..5a41cde 100644
--- a/erpnext/loan_management/doctype/pledge/pledge.py
+++ b/erpnext/loan_management/doctype/pledge/pledge.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class Pledge(Document):
pass
diff --git a/erpnext/loan_management/doctype/pledge/test_pledge.py b/erpnext/loan_management/doctype/pledge/test_pledge.py
index 2e01dc1..adcbc6e 100644
--- a/erpnext/loan_management/doctype/pledge/test_pledge.py
+++ b/erpnext/loan_management/doctype/pledge/test_pledge.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestPledge(unittest.TestCase):
pass
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 11333dc..efee701 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
@@ -3,11 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import nowdate
from frappe.model.document import Document
-from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_demand_loans,
- make_accrual_interest_entry_for_term_loans)
+from frappe.utils import nowdate
+
+from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (
+ make_accrual_interest_entry_for_demand_loans,
+ make_accrual_interest_entry_for_term_loans,
+)
+
class ProcessLoanInterestAccrual(Document):
def on_submit(self):
@@ -61,4 +66,3 @@
})
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 243a7a3..fcd0399 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,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Loan Interest Accrual']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/test_process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/test_process_loan_interest_accrual.py
index 6bfd3f4..e7d3602 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/test_process_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/test_process_loan_interest_accrual.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestProcessLoanInterestAccrual(unittest.TestCase):
pass
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 b4aad25..c3f5954 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
@@ -3,11 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import get_datetime
-from frappe import _
from frappe.model.document import Document
-from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import check_for_ltv_shortfall
+from frappe.utils import get_datetime
+
+from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import (
+ check_for_ltv_shortfall,
+)
+
class ProcessLoanSecurityShortfall(Document):
def onload(self):
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 dc9bd81..ced3bd7 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,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Loan Security Shortfall']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/test_process_loan_security_shortfall.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/test_process_loan_security_shortfall.py
index cd379a1..50e0a46 100644
--- a/erpnext/loan_management/doctype/process_loan_security_shortfall/test_process_loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/test_process_loan_security_shortfall.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestProcessLoanSecurityShortfall(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.py b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.py
index dfa5c79..5c125e1 100644
--- a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.py
+++ b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProposedPledge(Document):
pass
diff --git a/erpnext/loan_management/doctype/repayment_schedule/repayment_schedule.py b/erpnext/loan_management/doctype/repayment_schedule/repayment_schedule.py
index 2aa27b0..af9c669 100644
--- a/erpnext/loan_management/doctype/repayment_schedule/repayment_schedule.py
+++ b/erpnext/loan_management/doctype/repayment_schedule/repayment_schedule.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class RepaymentSchedule(Document):
pass
diff --git a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.py b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.py
index 9ee0b96..64be1b2 100644
--- a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.py
+++ b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class SalarySlipLoan(Document):
pass
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 74a1310..5660c42 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
@@ -3,9 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _
from frappe.model.document import Document
+
class SanctionedLoanAmount(Document):
def validate(self):
sanctioned_doc = frappe.db.exists('Sanctioned Loan Amount', {'applicant': self.applicant, 'company': self.company})
diff --git a/erpnext/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py b/erpnext/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py
index ba1372f..663f2e7 100644
--- a/erpnext/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py
+++ b/erpnext/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestSanctionedLoanAmount(unittest.TestCase):
pass
diff --git a/erpnext/loan_management/doctype/unpledge/unpledge.py b/erpnext/loan_management/doctype/unpledge/unpledge.py
index 205230a..2e82e23 100644
--- a/erpnext/loan_management/doctype/unpledge/unpledge.py
+++ b/erpnext/loan_management/doctype/unpledge/unpledge.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class Unpledge(Document):
pass
diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js
index 50b68da..43980ff 100644
--- a/erpnext/loan_management/loan_common.js
+++ b/erpnext/loan_management/loan_common.js
@@ -40,4 +40,4 @@
frm.set_value("applicant_name", null);
}
}
-});
\ No newline at end of file
+});
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 0ccd149..ff52702 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
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _
-from frappe.utils import get_datetime, flt
+from frappe.utils import flt
from six import iteritems
+import erpnext
+
+
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
@@ -136,4 +139,4 @@
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
\ No newline at end of file
+ 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 2a74a1e..c4adef1 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
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _
-from frappe.utils import flt, getdate, add_days
-from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
- import get_loan_security_details
+from frappe.utils import add_days, flt, getdate
+
+import erpnext
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure import (
+ get_loan_security_details,
+)
def execute(filters=None):
@@ -182,4 +185,4 @@
loan_wise_security_value[key[0]] += \
flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
- return loan_wise_security_value
\ No newline at end of file
+ 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 c6f6b99..9d8a425 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns()
data = get_data(filters)
@@ -126,4 +128,4 @@
data.append(row)
- return data
\ No newline at end of file
+ return data
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 887a86a..3d6242a 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
@@ -2,12 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import erpnext
+
from frappe import _
from frappe.utils import flt
from six import iteritems
-from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
- import get_loan_security_details, get_applicant_wise_total_loan_security_qty
+
+import erpnext
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure import (
+ get_applicant_wise_total_loan_security_qty,
+ get_loan_security_details,
+)
+
def execute(filters=None):
columns = get_columns(filters)
@@ -79,6 +84,3 @@
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 1951855..a93a381 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
index 546a68f..035290d 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
@@ -18,7 +18,7 @@
},
refresh: function (frm) {
setTimeout(() => {
- frm.toggle_display('generate_schedule', !(frm.is_new()));
+ frm.toggle_display('generate_schedule', !(frm.is_new() || frm.doc.docstatus));
frm.toggle_display('schedule', !(frm.is_new()));
}, 10);
},
@@ -69,10 +69,10 @@
if (flag) {
this.frm.add_custom_button(__('Maintenance Visit'), function () {
let options = "";
-
+
me.frm.call('get_pending_data', {data_type: "items"}).then(r => {
options = r.message;
-
+
let schedule_id = "";
let d = new frappe.ui.Dialog({
title: __("Enter Visit Details"),
@@ -86,7 +86,7 @@
let field = d.get_field("scheduled_date");
me.frm.call('get_pending_data',
{
- item_name: this.value,
+ item_name: this.value,
data_type: "date"
}).then(r => {
field.df.options = r.message;
@@ -157,10 +157,9 @@
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 d6e42f3..a1df9cf 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -2,22 +2,23 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _, throw
+from frappe.utils import add_days, cint, cstr, date_diff, formatdate, getdate
-from frappe.utils import add_days, getdate, cint, cstr, date_diff, formatdate
-
-from frappe import throw, _
-from erpnext.utilities.transaction_base import TransactionBase, delete_events
-from erpnext.stock.utils import get_valid_serial_nos
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.utils import get_valid_serial_nos
+from erpnext.utilities.transaction_base import TransactionBase, delete_events
+
class MaintenanceSchedule(TransactionBase):
@frappe.whitelist()
def generate_schedule(self):
+ if self.docstatus != 0:
+ return
self.set('schedules', [])
- frappe.db.sql("""delete from `tabMaintenance Schedule Detail`
- where parent=%s""", (self.name))
count = 1
for d in self.get('items'):
self.validate_maintenance_detail()
@@ -46,21 +47,21 @@
"Yearly": 365
}
for item in self.items:
- if item.periodicity and item.start_date:
+ if item.periodicity and item.periodicity != "Random" and item.start_date:
if not item.end_date:
if item.no_of_visits:
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
else:
item.end_date = add_days(item.start_date, days_in_period[item.periodicity])
-
+
diff = date_diff(item.end_date, item.start_date) + 1
no_of_visits = cint(diff / days_in_period[item.periodicity])
-
+
if not item.no_of_visits or item.no_of_visits == 0:
item.end_date = add_days(item.start_date, days_in_period[item.periodicity])
diff = date_diff(item.end_date, item.start_date) + 1
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])
@@ -207,7 +208,7 @@
def on_update(self):
frappe.db.set(self, 'status', 'Draft')
-
+
def update_amc_date(self, serial_nos, amc_expiry_date=None):
for serial_no in serial_nos:
serial_no_doc = frappe.get_doc("Serial No", serial_no)
@@ -300,7 +301,7 @@
for schedule in self.schedules:
if schedule.item_name == item_name and s_date == formatdate(schedule.scheduled_date, "dd-mm-yyyy"):
return schedule.name
-
+
@frappe.whitelist()
def update_serial_nos(s_id):
serial_nos = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'serial_no')
@@ -318,12 +319,12 @@
target.maintenance_type = "Scheduled"
target.maintenance_schedule = source.name
target.maintenance_schedule_detail = s_id
-
+
def update_sales(source, target, parent):
sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person')
target.service_person = sales_person
target.serial_no = ''
-
+
doclist = get_mapped_doc("Maintenance Schedule", source_name, {
"Maintenance Schedule": {
"doctype": "Maintenance Visit",
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.js
deleted file mode 100644
index e0f05b1..0000000
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Maintenance Schedule", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Maintenance Schedule
- () => frappe.tests.make('Maintenance Schedule', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
index 09981ba..38654de 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -2,11 +2,15 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-from frappe.utils.data import add_days, today, formatdate
-from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import make_maintenance_visit
+
+import unittest
import frappe
-import unittest
+from frappe.utils.data import add_days, formatdate, today
+
+from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import (
+ make_maintenance_visit,
+)
# test_records = frappe.get_test_records('Maintenance Schedule')
@@ -22,7 +26,7 @@
ms.cancel()
events_after_cancel = get_events(ms)
self.assertTrue(len(events_after_cancel) == 0)
-
+
def test_make_schedule(self):
ms = make_maintenance_schedule()
ms.save()
@@ -72,7 +76,7 @@
#checks if visit status is back updated in schedule
self.assertTrue(ms.schedules[1].completion_status, "Partially Completed")
-
+
def get_events(ms):
return frappe.get_all("Event Participants", filters={
"reference_doctype": ms.doctype,
diff --git a/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.json b/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.json
index 8ccef6a..afe273f 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.json
+++ b/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.json
@@ -89,13 +89,14 @@
"width": "160px"
},
{
+ "allow_on_submit": 1,
"columns": 2,
+ "default": "Pending",
"fieldname": "completion_status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Completion Status",
- "options": "Pending\nPartially Completed\nFully Completed",
- "read_only": 1
+ "options": "Pending\nPartially Completed\nFully Completed"
},
{
"fieldname": "column_break_3",
@@ -125,10 +126,11 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-05-27 16:07:25.905015",
+ "modified": "2021-09-16 21:25:22.506485",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Schedule Detail",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
diff --git a/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.py b/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.py
index e69b4fb..27c95a1 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule_detail/maintenance_schedule_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MaintenanceScheduleDetail(Document):
pass
diff --git a/erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.py b/erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.py
index 1dd47fe..9c4a690 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MaintenanceScheduleItem(Document):
pass
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
index 53ecdf5..7f98354 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
@@ -31,8 +31,8 @@
},
onload: function (frm, cdt, cdn) {
let item = locals[cdt][cdn];
- if (frm.maintenance_type == 'Scheduled') {
- let schedule_id = item.purposes[0].prevdoc_detail_docname;
+ if (frm.doc.maintenance_type === "Scheduled") {
+ const schedule_id = item.purposes[0].prevdoc_detail_docname || frm.doc.maintenance_schedule_detail;
frappe.call({
method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.update_serial_nos",
args: {
@@ -43,6 +43,9 @@
}
});
}
+ else {
+ frm.clear_table("purposes");
+ }
if (!frm.doc.status) {
frm.set_value({ status: 'Draft' });
@@ -73,12 +76,16 @@
if (this.frm.doc.docstatus === 0) {
this.frm.add_custom_button(__('Maintenance Schedule'),
function () {
+ if (!me.frm.doc.customer) {
+ frappe.msgprint(__('Please select Customer first'));
+ return;
+ }
erpnext.utils.map_current_doc({
method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit",
source_doctype: "Maintenance Schedule",
target: me.frm,
setters: {
- customer: me.frm.doc.customer || undefined,
+ customer: me.frm.doc.customer,
},
get_query_filters: {
docstatus: 1,
@@ -104,12 +111,16 @@
}, __("Get Items From"));
this.frm.add_custom_button(__('Sales Order'),
function () {
+ if (!me.frm.doc.customer) {
+ frappe.msgprint(__('Please select Customer first'));
+ return;
+ }
erpnext.utils.map_current_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit",
source_doctype: "Sales Order",
target: me.frm,
setters: {
- customer: me.frm.doc.customer || undefined,
+ customer: me.frm.doc.customer,
},
get_query_filters: {
docstatus: 1,
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
index 7fffc94..814ec0c 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import get_datetime
from erpnext.utilities.transaction_base import TransactionBase
+
class MaintenanceVisit(TransactionBase):
def get_feed(self):
return _("To {0}").format(self.customer_name)
@@ -28,11 +30,11 @@
def validate(self):
self.validate_serial_no()
self.validate_maintenance_date()
-
+
def update_completion_status(self):
if self.maintenance_schedule_detail:
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'completion_status', self.completion_status)
-
+
def update_actual_date(self):
if self.maintenance_schedule_detail:
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'actual_date', self.mntc_date)
diff --git a/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
index 2bea8d1..57e728d 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/test_maintenance_visit.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Maintenance Visit')
diff --git a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py
index a7f0f5b..4c59562 100644
--- a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py
+++ b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class MaintenanceVisitPurpose(Document):
pass
diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py
index 7ba43d6..1bc12ff 100644
--- a/erpnext/manufacturing/dashboard_fixtures.py
+++ b/erpnext/manufacturing/dashboard_fixtures.py
@@ -1,9 +1,14 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import frappe, erpnext, json
+import json
+
+import frappe
from frappe import _
-from frappe.utils import nowdate, get_first_day, get_last_day, add_months
+from frappe.utils import add_months, nowdate
+
+import erpnext
+
def get_data():
return frappe._dict({
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
index f19a1b0..d3bb33e 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
@@ -85,5 +85,3 @@
frm.trigger('set_tc_name_filter');
}
});
-
-
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index d7556ad..59eb168 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, getdate
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
+from frappe.utils import flt, getdate
+
from erpnext.stock.doctype.item.item import get_item_defaults
@@ -76,4 +78,4 @@
"postprocess": update_item
}
})
- return target_doc
\ No newline at end of file
+ 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 d9aa0ca..83260ec 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.js
deleted file mode 100644
index 51a0d94..0000000
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Blanket Order", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Blanket Order
- () => frappe.tests.make('Blanket Order', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index 3171def..3104ae0 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -3,12 +3,16 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import add_months, today
+
from erpnext import get_company_currency
+
from .blanket_order import make_order
+
class TestBlanketOrder(unittest.TestCase):
def setUp(self):
frappe.flags.args = frappe._dict()
@@ -88,4 +92,4 @@
bo.insert()
bo.submit()
- return bo
\ No newline at end of file
+ return bo
diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
index f07f3c8..0825f76 100644
--- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
+++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class BlanketOrderItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index 3f50b41..5f5c20a 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -215,7 +215,32 @@
label: __('Qty To Manufacture'),
fieldname: 'qty',
reqd: 1,
- default: 1
+ default: 1,
+ onchange: () => {
+ const { quantity, items: rm } = frm.doc;
+ const variant_items_map = rm.reduce((acc, item) => {
+ acc[item.item_code] = item.qty;
+ return acc;
+ }, {});
+ const mf_qty = cur_dialog.fields_list.filter(
+ (f) => f.df.fieldname === "qty"
+ )[0]?.value;
+ const items = cur_dialog.fields.filter(
+ (f) => f.fieldname === "items"
+ )[0]?.data;
+
+ if (!items) {
+ return;
+ }
+
+ items.forEach((item) => {
+ item.qty =
+ (variant_items_map[item.item_code] * mf_qty) /
+ quantity;
+ });
+
+ cur_dialog.refresh();
+ }
});
}
@@ -235,7 +260,7 @@
reqd: 1,
},
{
- fieldname: "varint_item_code",
+ fieldname: "variant_item_code",
options: "Item",
label: __("Variant Item"),
fieldtype: "Link",
@@ -287,7 +312,7 @@
let variant_items = data.items || [];
variant_items.forEach(d => {
- if (!d.varint_item_code) {
+ if (!d.variant_item_code) {
frappe.throw(__("Select variant item code for the template item {0}", [d.item_code]));
}
})
@@ -299,7 +324,7 @@
has_template_rm.forEach(d => {
dialog.fields_dict.items.df.data.push({
"item_code": d.item_code,
- "varint_item_code": "",
+ "variant_item_code": "",
"qty": d.qty,
"source_warehouse": d.source_warehouse,
"operation": d.operation
@@ -446,6 +471,11 @@
},
callback: function(r) {
d = locals[cdt][cdn];
+ if (d.is_process_loss) {
+ r.message.rate = 0;
+ r.message.base_rate = 0;
+ }
+
$.extend(d, r.message);
refresh_field("items");
refresh_field("scrap_items");
@@ -655,3 +685,58 @@
frm.set_value("operations", []);
}
});
+
+frappe.tour['BOM'] = [
+ {
+ fieldname: "item",
+ title: "Item",
+ description: __("Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically.")
+ },
+ {
+ fieldname: "quantity",
+ title: "Quantity",
+ description: __("Enter the quantity of the Item that will be manufactured from this Bill of Materials.")
+ },
+ {
+ fieldname: "with_operations",
+ title: "With Operations",
+ description: __("To add Operations tick the 'With Operations' checkbox.")
+ },
+ {
+ fieldname: "items",
+ title: "Raw Materials",
+ description: __("Select the raw materials (Items) required to manufacture the Item")
+ }
+];
+
+frappe.ui.form.on("BOM Scrap Item", {
+ item_code(frm, cdt, cdn) {
+ const { item_code } = locals[cdt][cdn];
+ if (item_code === frm.doc.item) {
+ locals[cdt][cdn].is_process_loss = 1;
+ trigger_process_loss_qty_prompt(frm, cdt, cdn, item_code);
+ }
+ },
+});
+
+function trigger_process_loss_qty_prompt(frm, cdt, cdn, item_code) {
+ frappe.prompt(
+ {
+ fieldname: "percent",
+ fieldtype: "Percent",
+ label: __("% Finished Item Quantity"),
+ description:
+ __("Set quantity of process loss item:") +
+ ` ${item_code} ` +
+ __("as a percentage of finished item quantity"),
+ },
+ (data) => {
+ const row = locals[cdt][cdn];
+ row.stock_qty = (frm.doc.quantity * data.percent) / 100;
+ row.qty = row.stock_qty / (row.conversion_factor || 1);
+ refresh_field("scrap_items");
+ },
+ __("Set Process Loss Item Quantity"),
+ __("Set Quantity")
+ );
+}
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 0ba8507..232e3a0 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1,23 +1,22 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from typing import List
-from collections import deque
-import frappe, erpnext
-from frappe.utils import cint, cstr, flt, today
-from frappe import _
-from erpnext.setup.utils import get_exchange_rate
-from frappe.website.website_generator import WebsiteGenerator
-from erpnext.stock.get_item_details import get_conversion_factor
-from erpnext.stock.get_item_details import get_price_list_rate
-from frappe.core.doctype.version.version import get_diff
-from erpnext.controllers.queries import get_match_cond
-from erpnext.stock.doctype.item.item import get_item_details
-from frappe.model.mapper import get_mapped_doc
-
import functools
-
+from collections import deque
from operator import itemgetter
+from typing import List
+
+import frappe
+from frappe import _
+from frappe.core.doctype.version.version import get_diff
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, cstr, flt, today
+from frappe.website.website_generator import WebsiteGenerator
+
+import erpnext
+from erpnext.setup.utils import get_exchange_rate
+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"
@@ -148,6 +147,7 @@
self.set_plc_conversion_rate()
self.validate_uom_is_interger()
self.set_bom_material_details()
+ self.set_bom_scrap_items_detail()
self.validate_materials()
self.set_routing_operations()
self.validate_operations()
@@ -155,6 +155,7 @@
self.update_stock_qty()
self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate = False, save=False)
self.set_bom_level()
+ self.validate_scrap_items()
def get_context(self, context):
context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
@@ -200,7 +201,7 @@
def set_bom_material_details(self):
for item in self.get("items"):
- self.validate_bom_currecny(item)
+ self.validate_bom_currency(item)
ret = self.get_bom_material_detail({
"company": self.company,
@@ -219,6 +220,19 @@
if not item.get(r):
item.set(r, ret[r])
+ def set_bom_scrap_items_detail(self):
+ for item in self.get("scrap_items"):
+ args = {
+ "item_code": item.item_code,
+ "company": self.company,
+ "scrap_items": True,
+ "bom_no": '',
+ }
+ ret = self.get_bom_material_detail(args)
+ for key, value in ret.items():
+ if item.get(key) is None:
+ item.set(key, value)
+
@frappe.whitelist()
def get_bom_material_detail(self, args=None):
""" Get raw material details like uom, desc and rate"""
@@ -255,7 +269,7 @@
return ret_item
- def validate_bom_currecny(self, 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))
@@ -432,25 +446,29 @@
frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
check_list.append(m)
- def check_recursion(self, bom_list=[]):
+ def check_recursion(self, bom_list=None):
""" 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))
+
bom_list = self.traverse_tree()
- bom_nos = frappe.get_all('BOM Item', fields=["bom_no"],
- filters={'parent': ('in', bom_list), 'parenttype': 'BOM'})
+ child_items = frappe.get_all('BOM Item', fields=["bom_no", "item_code"],
+ filters={'parent': ('in', bom_list), 'parenttype': 'BOM'}) or []
- raise_exception = False
- if bom_nos and self.name in [d.bom_no for d in bom_nos]:
- raise_exception = True
+ child_bom = {d.bom_no for d in child_items}
+ child_items_codes = {d.item_code for d in child_items}
- if not raise_exception:
- bom_nos = frappe.get_all('BOM Item', fields=["parent"],
- filters={'bom_no': self.name, 'parenttype': 'BOM'})
+ if self.name in child_bom:
+ _throw_error(self.name)
- if self.name in [d.parent for d in bom_nos]:
- raise_exception = True
+ if self.item in child_items_codes:
+ _throw_error(self.item)
- if raise_exception:
- frappe.throw(_("BOM recursion: {0} cannot be parent or child of {1}").format(self.name, self.name))
+ 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}:
+ _throw_error(self.name)
def traverse_tree(self, bom_list=None):
def _get_children(bom_no):
@@ -492,27 +510,39 @@
if d.workstation:
self.update_rate_and_time(d, update_hour_rate)
- self.operating_cost += flt(d.operating_cost)
- self.base_operating_cost += flt(d.base_operating_cost)
+ operating_cost = d.operating_cost
+ base_operating_cost = d.base_operating_cost
+ if d.set_cost_based_on_bom_qty:
+ operating_cost = flt(d.cost_per_unit) * flt(self.quantity)
+ base_operating_cost = flt(d.base_cost_per_unit) * flt(self.quantity)
+
+ 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):
if not row.hour_rate or update_hour_rate:
hour_rate = flt(frappe.get_cached_value("Workstation", row.workstation, "hour_rate"))
- row.hour_rate = (hour_rate / flt(self.conversion_rate)
- if self.conversion_rate and hour_rate else hour_rate)
+
+ if hour_rate:
+ row.hour_rate = (hour_rate / flt(self.conversion_rate)
+ if self.conversion_rate and hour_rate else hour_rate)
if self.routing:
- row.time_in_mins = flt(frappe.db.get_value("BOM Operation", {
+ time_in_mins = flt(frappe.db.get_value("BOM Operation", {
"workstation": row.workstation,
"operation": row.operation,
- "sequence_id": row.sequence_id,
"parent": self.routing
}, ["time_in_mins"]))
+ if time_in_mins:
+ row.time_in_mins = time_in_mins
+
if row.hour_rate and row.time_in_mins:
row.base_hour_rate = flt(row.hour_rate) * flt(self.conversion_rate)
row.operating_cost = flt(row.hour_rate) * flt(row.time_in_mins) / 60.0
row.base_operating_cost = flt(row.operating_cost) * flt(self.conversion_rate)
+ row.cost_per_unit = row.operating_cost / (row.batch_size or 1.0)
+ row.base_cost_per_unit = row.base_operating_cost / (row.batch_size or 1.0)
if update_hour_rate:
row.db_update()
@@ -691,6 +721,32 @@
if update:
self.db_set("bom_level", self.bom_level)
+ def validate_scrap_items(self):
+ 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))
+ 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))
+
+ 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))
+
+ 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))
+
+ 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))
+
+ if msg:
+ frappe.throw(msg, title=_("Note"))
+
def get_bom_item_rate(args, bom_doc):
if bom_doc.rm_cost_as_per == 'Valuation Rate':
rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1)
@@ -808,8 +864,11 @@
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_stock_item=is_stock_item, qty_field="stock_qty")
+ 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"
+ )
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
else:
diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py
index 361826e..f65df01 100644
--- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py
+++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'bom_no',
diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
index 6088e46..e614a7e 100644
--- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html
+++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
@@ -38,4 +38,4 @@
{{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
{% endif %}
</p>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js
index 60fb377..6e2599e 100644
--- a/erpnext/manufacturing/doctype/bom/bom_tree.js
+++ b/erpnext/manufacturing/doctype/bom/bom_tree.js
@@ -70,4 +70,4 @@
}
},
view_template: 'bom_item_preview'
-}
\ No newline at end of file
+}
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.js b/erpnext/manufacturing/doctype/bom/test_bom.js
index 5044a28..98a9198 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.js
+++ b/erpnext/manufacturing/doctype/bom/test_bom.js
@@ -60,4 +60,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index c89f7d6..4c03230 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -2,16 +2,21 @@
# License: GNU General Public License v3. See license.txt
-from collections import deque
import unittest
+from collections import deque
+from functools import partial
+
import frappe
-from frappe.utils import cstr, flt
from frappe.test_runner import make_test_records
-from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
-from erpnext.manufacturing.doctype.bom.bom import make_variant_bom
+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.stock.doctype.item.test_item import make_item
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+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')
@@ -104,6 +109,24 @@
self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
+ def test_bom_cost_with_batch_size(self):
+ bom = frappe.copy_doc(test_records[2])
+ bom.docstatus = 0
+ op_cost = 0.0
+ for op_row in bom.operations:
+ op_row.docstatus = 0
+ op_row.batch_size = 2
+ op_row.set_cost_based_on_bom_qty = 1
+ op_cost += op_row.operating_cost
+
+ bom.save()
+
+ 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)
+ 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)):
@@ -280,13 +303,92 @@
self.assertEqual(reqd_item.qty, created_item.qty)
self.assertEqual(reqd_item.exploded_qty, created_item.exploded_qty)
+ 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})
+
+ 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()
+
+ 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})
+
+ 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))
+
+ with self.assertRaises(frappe.ValidationError) as err:
+ 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()
+
+ if not frappe.db.exists("BOM", f"BOM-{fg_item_non_whole.item_code}-001"):
+ bom_doc = create_bom_with_process_loss_item(
+ fg_item_non_whole, bom_item, scrap_qty=0.25, scrap_rate=0, fg_qty=1
+ )
+ bom_doc.submit()
+
+ bom_doc = create_bom_with_process_loss_item(
+ fg_item_non_whole, bom_item, scrap_qty=2, scrap_rate=0
+ )
+ # PL Item qty can't be >= FG Item qty
+ self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+ bom_doc = create_bom_with_process_loss_item(
+ fg_item_non_whole, bom_item, scrap_qty=1, scrap_rate=100
+ )
+ # PL Item rate has to be 0
+ self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+ bom_doc = create_bom_with_process_loss_item(
+ fg_item_whole, bom_item, scrap_qty=0.25, scrap_rate=0
+ )
+ # Items with whole UOMs can't be PL Items
+ self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+ bom_doc = create_bom_with_process_loss_item(
+ fg_item_non_whole, bom_item, scrap_qty=0.25, scrap_rate=0, is_process_loss=0
+ )
+ # FG Items in Scrap/Loss Table should have Is Process Loss set
+ self.assertRaises(frappe.ValidationError, bom_doc.submit)
+
+ def test_bom_item_query(self):
+ query = partial(item_query, doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters={"is_stock_item": 1})
+
+ test_items = query(txt="_Test")
+ filtered = query(txt="_Test Item 2")
+
+ self.assertNotEqual(len(test_items), len(filtered), msg="Item filtering showing excessive results")
+ self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
+
def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
-
-
-
def level_order_traversal(node):
traversal = []
q = deque()
@@ -332,6 +434,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.currency = "INR"
bom.insert()
bom.submit()
@@ -353,3 +456,45 @@
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):
+ 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.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")
+ ]
+ 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}
+ )
+ else:
+ return frappe.get_doc("Item", item_code)
diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py
index cc5a3f8..4317d3a 100644
--- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py
+++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class BOMExplosionItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.py b/erpnext/manufacturing/doctype/bom_item/bom_item.py
index e7cdea2..2954238 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.py
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class BOMItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index 4458e6d..ec617f3 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -8,15 +8,23 @@
"field_order": [
"sequence_id",
"operation",
- "workstation",
- "description",
"col_break1",
- "hour_rate",
+ "workstation",
"time_in_mins",
- "operating_cost",
+ "costing_section",
+ "hour_rate",
"base_hour_rate",
+ "column_break_9",
+ "operating_cost",
"base_operating_cost",
+ "column_break_11",
"batch_size",
+ "set_cost_based_on_bom_qty",
+ "cost_per_unit",
+ "base_cost_per_unit",
+ "more_information_section",
+ "description",
+ "column_break_18",
"image"
],
"fields": [
@@ -117,13 +125,59 @@
"fieldname": "sequence_id",
"fieldtype": "Int",
"label": "Sequence ID"
+ },
+ {
+ "depends_on": "eval:doc.batch_size > 0 && doc.set_cost_based_on_bom_qty",
+ "fieldname": "cost_per_unit",
+ "fieldtype": "Float",
+ "label": "Cost Per Unit",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "base_cost_per_unit",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "label": "Base Cost Per Unit",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "costing_section",
+ "fieldtype": "Section Break",
+ "label": "Costing"
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "more_information_section",
+ "fieldtype": "Section Break",
+ "label": "More Information"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "set_cost_based_on_bom_qty",
+ "fieldtype": "Check",
+ "label": "Set Operating Cost Based On BOM Quantity"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-01-12 14:48:09.596843",
+ "modified": "2021-09-13 16:45:01.092868",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Operation",
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.py b/erpnext/manufacturing/doctype/bom_operation/bom_operation.py
index ee3f877..5e46c7e 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.py
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class BOMOperation(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
index 9f7091d..7018082 100644
--- a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
+++ b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
@@ -1,345 +1,112 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2016-09-26 02:19:21.642081",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
+ "actions": [],
+ "creation": "2016-09-26 02:19:21.642081",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "item_code",
+ "column_break_2",
+ "item_name",
+ "is_process_loss",
+ "quantity_and_rate",
+ "stock_qty",
+ "rate",
+ "amount",
+ "column_break_6",
+ "stock_uom",
+ "base_rate",
+ "base_amount"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_code",
- "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": "Item Code",
- "length": 0,
- "no_copy": 0,
- "options": "Item",
- "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": "item_code",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Code",
+ "options": "Item",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item_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": "Item 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": "item_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Item Name"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "quantity_and_rate",
- "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": "Quantity and Rate",
- "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": "quantity_and_rate",
+ "fieldtype": "Section Break",
+ "label": "Quantity and Rate"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "stock_qty",
- "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": "Qty",
- "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": "stock_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Qty",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "rate",
- "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": "Rate",
- "length": 0,
- "no_copy": 0,
- "options": "currency",
- "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": "rate",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Rate",
+ "options": "currency"
+ },
{
- "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": 0,
- "in_standard_filter": 0,
- "label": "Amount",
- "length": 0,
- "no_copy": 0,
- "options": "currency",
- "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
- },
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "label": "Amount",
+ "options": "currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_6",
- "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_6",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "stock_uom",
- "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": "Stock UOM",
- "length": 0,
- "no_copy": 0,
- "options": "UOM",
- "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
- },
+ "fieldname": "stock_uom",
+ "fieldtype": "Link",
+ "label": "Stock UOM",
+ "options": "UOM",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "base_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Basic Rate (Company Currency)",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
- "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": "base_rate",
+ "fieldtype": "Currency",
+ "label": "Basic Rate (Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "base_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Basic Amount (Company Currency)",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
- "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": "base_amount",
+ "fieldtype": "Currency",
+ "label": "Basic Amount (Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_process_loss",
+ "fieldtype": "Check",
+ "label": "Is Process Loss"
}
- ],
- "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-07-04 16:04:32.442287",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "BOM Scrap Item",
- "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": "2021-06-22 16:46:12.153311",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "BOM Scrap Item",
+ "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/manufacturing/doctype/bom_scrap_item/bom_scrap_item.py b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.py
index b6d423f..891fc53 100644
--- a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.py
+++ b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class BOMScrapItem(Document):
pass
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 e4b8a20..bf5fe2e 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js
@@ -46,4 +46,4 @@
}
});
}
-});
\ No newline at end of file
+});
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 8fbcd4e..ed71c6d 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -3,13 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.utils import cstr, flt
-from frappe import _
-from six import string_types
-from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order
-from frappe.model.document import Document
+
+import json
+
import click
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import cstr, flt
+from six import string_types
+
+from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order
+
class BOMUpdateTool(Document):
def replace_bom(self):
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.js b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.js
deleted file mode 100644
index d220df2..0000000
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: BOM Update Tool", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('BOM Update Tool', [
- // insert a new BOM Update Tool
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 80d1cdf..88c69ce 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
@@ -3,11 +3,14 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
+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')
diff --git a/erpnext/manufacturing/doctype/bom_website_item/bom_website_item.py b/erpnext/manufacturing/doctype/bom_website_item/bom_website_item.py
index 4088a7f..f627b4e 100644
--- a/erpnext/manufacturing/doctype/bom_website_item/bom_website_item.py
+++ b/erpnext/manufacturing/doctype/bom_website_item/bom_website_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class BOMWebsiteItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.py b/erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.py
index bcc5dda..5bd8cf5 100644
--- a/erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.py
+++ b/erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class BOMWebsiteOperation(Document):
pass
diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py
index 56ec435..62833d7 100644
--- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py
+++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py
@@ -3,9 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import time_diff_in_hours
+
from frappe.model.document import Document
+from frappe.utils import time_diff_in_hours
+
class DowntimeEntry(Document):
def validate(self):
diff --git a/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py
index 8b2a8d3..37169f4 100644
--- a/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py
+++ b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestDowntimeEntry(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index 81860c9..35be388 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -26,15 +26,23 @@
refresh: function(frm) {
frappe.flags.pause_job = 0;
frappe.flags.resume_job = 0;
+ let has_items = frm.doc.items && frm.doc.items.length;
- if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) {
- if (frm.doc.for_quantity != frm.doc.transferred_qty) {
+ if (!frm.doc.__islocal && 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;
+
+ if (to_request || excess_transfer_allowed) {
frm.add_custom_button(__("Material Request"), () => {
frm.trigger("make_material_request");
});
}
- if (frm.doc.for_quantity != frm.doc.transferred_qty) {
+ // check if any row has untransferred materials
+ // in case of multiple items in JC
+ let to_transfer = frm.doc.items.some((row) => row.transferred_qty < row.required_qty);
+
+ if (to_transfer || excess_transfer_allowed) {
frm.add_custom_button(__("Material Transfer"), () => {
frm.trigger("make_stock_entry");
}).addClass("btn-primary");
@@ -367,4 +375,4 @@
to_time: function(frm) {
frm.set_value('started_time', '');
}
-})
\ No newline at end of file
+})
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index 046e2fd..7dd38f4 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -38,6 +38,8 @@
"total_time_in_mins",
"section_break_8",
"items",
+ "scrap_items_section",
+ "scrap_items",
"corrective_operation_section",
"for_job_card",
"is_corrective_job_card",
@@ -185,7 +187,7 @@
"default": "0",
"fieldname": "transferred_qty",
"fieldtype": "Float",
- "label": "Transferred Qty",
+ "label": "FG Qty from Transferred Raw Materials",
"read_only": 1
},
{
@@ -392,14 +394,28 @@
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch"
+ },
+ {
+ "fieldname": "scrap_items_section",
+ "fieldtype": "Section Break",
+ "label": "Scrap Items"
+ },
+ {
+ "fieldname": "scrap_items",
+ "fieldtype": "Table",
+ "label": "Scrap Items",
+ "no_copy": 1,
+ "options": "Job Card Scrap Item",
+ "print_hide": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2021-03-16 15:59:32.766484",
+ "modified": "2021-09-14 00:38:46.873105",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 66e2394..e1d79be 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -1,18 +1,31 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
import datetime
import json
-from frappe import _, bold
-from frappe.model.mapper import get_mapped_doc
-from frappe.model.document import Document
-from frappe.utils import (flt, cint, time_diff_in_hours, get_datetime, getdate,
- get_time, add_to_date, time_diff, add_days, get_datetime_str, get_link_to_form, time_diff_in_seconds)
-from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
+import frappe
+from frappe import _, bold
+from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import (
+ add_days,
+ add_to_date,
+ cint,
+ flt,
+ get_datetime,
+ get_link_to_form,
+ get_time,
+ getdate,
+ time_diff,
+ time_diff_in_hours,
+ time_diff_in_seconds,
+)
+
+from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import (
+ get_mins_between_operations,
+)
+
class OverlapError(frappe.ValidationError): pass
@@ -21,22 +34,26 @@
class JobCardCancelError(frappe.ValidationError): pass
class JobCard(Document):
+ def onload(self):
+ excess_transfer = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
+ self.set_onload("job_card_excess_transfer", excess_transfer)
+
def validate(self):
self.validate_time_logs()
self.set_status()
self.validate_operation_id()
self.validate_sequence_id()
- self.get_sub_operations()
+ self.set_sub_operations()
self.update_sub_operation_status()
- def get_sub_operations(self):
+ def set_sub_operations(self):
if self.operation:
self.sub_operations = []
- for row in frappe.get_all("Sub Operation",
- filters = {"parent": self.operation}, fields=["operation", "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
@@ -75,7 +92,7 @@
if args.get("employee"):
# override capacity for employee
production_capacity = 1
- validate_overlap_for = " and jc.employee = %(employee)s "
+ validate_overlap_for = " and jctl.employee = %(employee)s "
extra_cond = ''
if check_next_available_slot:
@@ -433,6 +450,7 @@
frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty))
def set_transferred_qty(self, update_status=False):
+ "Set total FG Qty for which RM was transferred."
if not self.items:
self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
@@ -441,6 +459,7 @@
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,
@@ -484,7 +503,9 @@
self.status = 'Work In Progress'
if (self.docstatus == 1 and
- (self.for_quantity == self.transferred_qty or not self.items)):
+ (self.for_quantity <= self.transferred_qty or not self.items)):
+ # consider excess transfer
+ # completed qty is checked via separate validation
self.status = 'Completed'
if self.status != 'Completed':
@@ -602,7 +623,11 @@
def set_missing_values(source, target):
target.purpose = "Material Transfer for Manufacture"
target.from_bom = 1
- target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
+
+ # avoid negative 'For Quantity'
+ pending_fg_qty = source.get('for_quantity', 0) - 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()
@@ -652,7 +677,7 @@
conditions = get_filters_cond("Job Card", filters, [])
job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
- `tabJob Card`.employee_name, `tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''),
+ `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`
@@ -662,7 +687,7 @@
for d in job_cards:
subject_data = []
- for field in ["name", "work_order", "remarks", "employee_name"]:
+ for field in ["name", "work_order", "remarks"]:
if not d.get(field): continue
subject_data.append(d.get(field))
@@ -690,7 +715,7 @@
target.set('time_logs', [])
target.set('employee', [])
target.set('items', [])
- target.get_sub_operations()
+ target.set_sub_operations()
target.get_required_items()
target.validate_time_logs()
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
index c2aa2bd..3ec6697 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'job_card',
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js
index ed851eb..8017209 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_list.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js
@@ -12,4 +12,4 @@
return [__("Open"), "red", "status,=,Open"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.js b/erpnext/manufacturing/doctype/job_card/test_job_card.js
deleted file mode 100644
index 5dc7805..0000000
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Job Card", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Job Card
- () => frappe.tests.make('Job Card', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index b6a6c33..ea5d364 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -1,75 +1,194 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-
import unittest
+
import frappe
from frappe.utils import random_string
-from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
+
+from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError, OverlapError
+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.job_card.job_card import OperationMismatchError
+from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
class TestJobCard(unittest.TestCase):
+
+ def setUp(self):
+ transfer_material_against, source_warehouse = None, None
+ tests_that_transfer_against_jc = ("test_job_card_multiple_materials_transfer",
+ "test_job_card_excess_material_transfer")
+
+ 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 tearDown(self):
+ frappe.db.rollback()
+
def test_job_card(self):
- data = frappe.get_cached_value('BOM',
- {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
- if data:
- bom, bom_item = data
+ job_cards = frappe.get_all('Job Card',
+ filters = {'work_order': self.work_order.name}, fields = ["operation_id", "name"])
- work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
+ if job_cards:
+ job_card = job_cards[0]
+ frappe.db.set_value("Job Card", job_card.name, "operation_row_number", job_card.operation_id)
- job_cards = frappe.get_all('Job Card',
- filters = {'work_order': work_order.name}, fields = ["operation_id", "name"])
+ doc = frappe.get_doc("Job Card", job_card.name)
+ doc.operation_id = "Test Data"
+ self.assertRaises(OperationMismatchError, doc.save)
- if job_cards:
- job_card = job_cards[0]
- frappe.db.set_value("Job Card", job_card.name, "operation_row_number", job_card.operation_id)
+ for d in job_cards:
+ frappe.delete_doc("Job Card", d.name)
- doc = frappe.get_doc("Job Card", job_card.name)
- doc.operation_id = "Test Data"
- self.assertRaises(OperationMismatchError, doc.save)
+ 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_card = job_cards[0]
+
+ if job_card:
+ 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.submit()
+
+ 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_with_different_work_station(self):
- data = frappe.get_cached_value('BOM',
- {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
+ def test_job_card_overlap(self):
+ wo2 = make_wo_order_test_record(item="_Test FG Item 2", qty=2)
- if data:
- bom, bom_item = data
+ 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})
- work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
+ jc1 = frappe.get_doc("Job Card", jc1_name)
+ jc2 = frappe.get_doc("Job Card", jc2_name)
- job_cards = frappe.get_all('Job Card',
- filters = {'work_order': work_order.name},
- fields = ["operation_id", "workstation", "name", "for_quantity"])
+ employee = "_T-Employee-00001" # from test records
- job_card = job_cards[0]
+ 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()
- if job_card:
- workstation = frappe.db.get_value("Workstation",
- {"name": ("not in", [job_card.workstation])}, "name")
+ # 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,
+ })
+ self.assertRaises(OverlapError, jc2.save)
- if not workstation or job_card.workstation == workstation:
- workstation = make_workstation(workstation_name=random_string(5)).name
+ 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
+ )
- 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.submit()
+ 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)
- completed_qty = frappe.db.get_value("Work Order Operation", job_card.operation_id, "completed_qty")
- self.assertEqual(completed_qty, job_card.for_quantity)
+ transfer_entry_1 = make_stock_entry_from_jc(job_card_name)
+ del transfer_entry_1.items[1] # transfer only 1 of 2 RMs
+ transfer_entry_1.insert()
+ transfer_entry_1.submit()
- doc.cancel()
+ job_card.reload()
- for d in job_cards:
- frappe.delete_doc("Job Card", d.name)
\ No newline at end of file
+ self.assertEqual(transfer_entry_1.fg_completed_qty, 2)
+ self.assertEqual(job_card.transferred_qty, 2)
+
+ # transfer second RM
+ transfer_entry_2 = make_stock_entry_from_jc(job_card_name)
+ del transfer_entry_2.items[0]
+ transfer_entry_2.insert()
+ transfer_entry_2.submit()
+
+ # 'For Quantity' here will be 0 since
+ # transfer was made for 2 fg qty in first transfer Stock Entry
+ self.assertEqual(transfer_entry_2.fg_completed_qty, 0)
+
+ 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)
+
+ 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)
+
+ # 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")
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.py b/erpnext/manufacturing/doctype/job_card_item/job_card_item.py
index 373cba2..a133836 100644
--- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.py
+++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class JobCardItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py
index 85d7298..43d1422 100644
--- a/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py
+++ b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class JobCardOperation(Document):
pass
diff --git a/erpnext/healthcare/doctype/organism_test_item/__init__.py b/erpnext/manufacturing/doctype/job_card_scrap_item/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/organism_test_item/__init__.py
rename to erpnext/manufacturing/doctype/job_card_scrap_item/__init__.py
diff --git a/erpnext/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.json b/erpnext/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.json
new file mode 100644
index 0000000..9e9f1c4
--- /dev/null
+++ b/erpnext/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.json
@@ -0,0 +1,82 @@
+{
+ "actions": [],
+ "creation": "2021-09-14 00:30:28.533884",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "item_code",
+ "item_name",
+ "column_break_3",
+ "description",
+ "quantity_and_rate",
+ "stock_qty",
+ "column_break_6",
+ "stock_uom"
+ ],
+ "fields": [
+ {
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Scrap Item Code",
+ "options": "Item",
+ "reqd": 1
+ },
+ {
+ "fetch_from": "item_code.item_name",
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Scrap Item Name"
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "item_code.description",
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Description",
+ "read_only": 1
+ },
+ {
+ "fieldname": "quantity_and_rate",
+ "fieldtype": "Section Break",
+ "label": "Quantity and Rate"
+ },
+ {
+ "fieldname": "stock_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Qty",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_6",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "item_code.stock_uom",
+ "fieldname": "stock_uom",
+ "fieldtype": "Link",
+ "label": "Stock UOM",
+ "options": "UOM",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-14 01:20:48.588052",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Job Card Scrap Item",
+ "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/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.py b/erpnext/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.py
new file mode 100644
index 0000000..372df1b
--- /dev/null
+++ b/erpnext/manufacturing/doctype/job_card_scrap_item/job_card_scrap_item.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from frappe.model.document import Document
+
+
+class JobCardScrapItem(Document):
+ pass
diff --git a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py
index 3dc6689..ed27e7f 100644
--- a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py
+++ b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class JobCardTimeLog(Document):
pass
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js
index 668e981..a0122a4 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js
@@ -30,4 +30,4 @@
title: __("Update BOM Cost Automatically"),
description: __("If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.")
}
-];
\ No newline at end of file
+];
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index 024f784..01647d5 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -25,9 +25,12 @@
"overproduction_percentage_for_sales_order",
"column_break_16",
"overproduction_percentage_for_work_order",
+ "job_card_section",
+ "add_corrective_operation_cost_in_finished_good_valuation",
+ "column_break_24",
+ "job_card_excess_transfer",
"other_settings_section",
"update_bom_costs_automatically",
- "add_corrective_operation_cost_in_finished_good_valuation",
"column_break_23",
"make_serial_no_batch_from_work_order"
],
@@ -96,10 +99,10 @@
},
{
"default": "0",
- "description": "Allow multiple material consumptions against a Work Order",
+ "description": "Allow material consumptions without immediately manufacturing finished goods against a Work Order",
"fieldname": "material_consumption",
"fieldtype": "Check",
- "label": "Allow Multiple Material Consumption"
+ "label": "Allow Continuous Material Consumption"
},
{
"default": "0",
@@ -175,13 +178,29 @@
"fieldname": "add_corrective_operation_cost_in_finished_good_valuation",
"fieldtype": "Check",
"label": "Add Corrective Operation Cost in Finished Good Valuation"
+ },
+ {
+ "fieldname": "job_card_section",
+ "fieldtype": "Section Break",
+ "label": "Job Card"
+ },
+ {
+ "fieldname": "column_break_24",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "description": "Allow transferring raw materials even after the Required Quantity is fulfilled",
+ "fieldname": "job_card_excess_transfer",
+ "fieldtype": "Check",
+ "label": "Allow Excess Material Transfer"
}
],
"icon": "icon-wrench",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-03-16 15:54:38.967341",
+ "modified": "2021-09-13 22:09:09.401559",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
index e88164f..18d78b5 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+from dateutil.relativedelta import relativedelta
from frappe.model.document import Document
from frappe.utils import cint
-from dateutil.relativedelta import relativedelta
+
class ManufacturingSettings(Document):
pass
@@ -20,4 +22,4 @@
frappe.local.material_consumption = cint(frappe.db.get_single_value('Manufacturing Settings',
'material_consumption'))
- return frappe.local.material_consumption
\ No newline at end of file
+ return frappe.local.material_consumption
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.js
deleted file mode 100644
index 2b2589e..0000000
--- a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Manufacturing Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Manufacturing Settings', [
- // insert a new Manufacturing Settings
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py
index 7391f65..fd0ac72 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/test_manufacturing_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestManufacturingSettings(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index 6c60bbd..27d7c41 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -6,17 +6,17 @@
"engine": "InnoDB",
"field_order": [
"item_code",
- "item_name",
- "material_request_type",
"from_warehouse",
"warehouse",
- "column_break_4",
+ "item_name",
+ "material_request_type",
+ "actual_qty",
+ "ordered_qty",
"required_bom_qty",
+ "column_break_4",
"quantity",
"uom",
"projected_qty",
- "actual_qty",
- "ordered_qty",
"reserved_qty_for_production",
"safety_stock",
"item_details",
@@ -28,6 +28,7 @@
],
"fields": [
{
+ "columns": 2,
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
@@ -41,6 +42,7 @@
"label": "Item Name"
},
{
+ "columns": 2,
"fieldname": "warehouse",
"fieldtype": "Link",
"in_list_view": 1,
@@ -50,10 +52,11 @@
"reqd": 1
},
{
+ "columns": 1,
"fieldname": "material_request_type",
"fieldtype": "Select",
"in_list_view": 1,
- "label": "Material Request Type",
+ "label": "Type",
"options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
},
{
@@ -61,10 +64,11 @@
"fieldtype": "Column Break"
},
{
+ "columns": 1,
"fieldname": "quantity",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Required Quantity",
+ "label": "Plan to Request Qty",
"no_copy": 1,
"reqd": 1
},
@@ -75,11 +79,12 @@
"read_only": 1
},
{
+ "columns": 2,
"default": "0",
"fieldname": "actual_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Actual Qty",
+ "label": "Available Qty",
"no_copy": 1,
"read_only": 1
},
@@ -157,16 +162,18 @@
"read_only": 1
},
{
+ "columns": 2,
"fieldname": "required_bom_qty",
"fieldtype": "Float",
- "label": "Required Qty as per BOM",
+ "in_list_view": 1,
+ "label": "Qty As Per BOM",
"no_copy": 1,
"read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2021-03-26 12:41:13.013149",
+ "modified": "2021-08-23 18:17:58.400462",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.py b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.py
index 73e369c..bc26644 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.py
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class MaterialRequestPlanItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.js b/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.js
deleted file mode 100644
index 14c6e39..0000000
--- a/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Material Request Plan Item", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Material Request Plan Item
- () => frappe.tests.make('Material Request Plan Item', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.py b/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.py
index dc43b69..2675af9 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.py
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/test_material_request_plan_item.py
@@ -5,5 +5,6 @@
import unittest
+
class TestMaterialRequestPlanItem(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/operation/operation.js b/erpnext/manufacturing/doctype/operation/operation.js
index 102b678..ea73fd6 100644
--- a/erpnext/manufacturing/doctype/operation/operation.js
+++ b/erpnext/manufacturing/doctype/operation/operation.js
@@ -11,4 +11,22 @@
};
});
}
-});
\ No newline at end of file
+});
+
+frappe.tour['Operation'] = [
+ {
+ fieldname: "__newname",
+ title: "Operation Name",
+ description: __("Enter a name for the Operation, for example, Cutting.")
+ },
+ {
+ fieldname: "workstation",
+ title: "Default Workstation",
+ description: __("Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders.")
+ },
+ {
+ fieldname: "sub_operations",
+ title: "Sub Operations",
+ description: __("If an operation is divided into sub operations, they can be added here.")
+ }
+];
diff --git a/erpnext/manufacturing/doctype/operation/operation.py b/erpnext/manufacturing/doctype/operation/operation.py
index 374f320..2926f91 100644
--- a/erpnext/manufacturing/doctype/operation/operation.py
+++ b/erpnext/manufacturing/doctype/operation/operation.py
@@ -7,6 +7,7 @@
from frappe import _
from frappe.model.document import Document
+
class Operation(Document):
def validate(self):
if not self.description:
diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py
index 8deb9ec..284fd9d 100644
--- a/erpnext/manufacturing/doctype/operation/operation_dashboard.py
+++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'operation',
diff --git a/erpnext/manufacturing/doctype/operation/test_operation.py b/erpnext/manufacturing/doctype/operation/test_operation.py
index 0067231..2b24118 100644
--- a/erpnext/manufacturing/doctype/operation/test_operation.py
+++ b/erpnext/manufacturing/doctype/operation/test_operation.py
@@ -2,9 +2,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
test_records = frappe.get_test_records('Operation')
class TestOperation(unittest.TestCase):
@@ -28,4 +29,4 @@
return doc
except frappe.DuplicateEntryError:
- return frappe.get_doc("Operation", args.operation)
\ No newline at end of file
+ return frappe.get_doc("Operation", args.operation)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index d198a69..2bd02da 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -242,6 +242,8 @@
},
get_sub_assembly_items: function(frm) {
+ frm.dirty();
+
frappe.call({
method: "get_sub_assembly_items",
freeze: true,
@@ -254,7 +256,7 @@
get_items_for_mr: function(frm) {
if (!frm.doc.for_warehouse) {
- frappe.throw(__("Select warehouse for material requests"));
+ frappe.throw(__("To make material requests, 'Make Material Request for Warehouse' field is mandatory"));
}
if (frm.doc.ignore_existing_ordered_qty) {
@@ -265,9 +267,18 @@
title: title,
fields: [
{
- "fieldtype": "Table MultiSelect", "label": __("Source Warehouses (Optional)"),
- "fieldname": "warehouses", "options": "Production Plan Material Request Warehouse",
- "description": __("System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase."),
+ 'label': __('Target Warehouse'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'target_warehouse',
+ 'read_only': true,
+ 'default': frm.doc.for_warehouse
+ },
+ {
+ 'label': __('Source Warehouses (Optional)'),
+ 'fieldtype': 'Table MultiSelect',
+ 'fieldname': 'warehouses',
+ 'options': 'Production Plan Material Request Warehouse',
+ 'description': __('If source warehouse selected then system will create the material request with type Material Transfer from Source to Target warehouse. If not selected then will create the material request with type Purchase for the target warehouse.'),
get_query: function () {
return {
filters: {
@@ -342,7 +353,11 @@
frappe.prompt(fields, (row) => {
let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
- open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses: row.warehouses });
+ open_url_post(frappe.request.url, {
+ cmd: get_template_url,
+ doc: frm.doc,
+ warehouses: row.warehouses
+ });
}, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock'));
},
@@ -421,6 +436,25 @@
}
});
+frappe.ui.form.on("Production Plan Sales Order", {
+ sales_order(frm, cdt, cdn) {
+ const { sales_order } = locals[cdt][cdn];
+ if (!sales_order) {
+ return;
+ }
+ frappe.call({
+ method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_so_details",
+ args: { sales_order },
+ callback(r) {
+ const {transaction_date, customer, grand_total} = r.message;
+ frappe.model.set_value(cdt, cdn, 'sales_order_date', transaction_date);
+ frappe.model.set_value(cdt, cdn, 'customer', customer);
+ frappe.model.set_value(cdt, cdn, 'grand_total', grand_total);
+ }
+ });
+ }
+});
+
cur_frm.fields_dict['sales_orders'].grid.get_field("sales_order").get_query = function() {
return{
filters: [
@@ -428,3 +462,36 @@
]
}
};
+
+frappe.tour['Production Plan'] = [
+ {
+ fieldname: "get_items_from",
+ title: "Get Items From",
+ description: __("Select whether to get items from a Sales Order or a Material Request. For now select <b>Sales Order</b>.\n A Production Plan can also be created manually where you can select the Items to manufacture.")
+ },
+ {
+ fieldname: "get_sales_orders",
+ title: "Get Sales Orders",
+ description: __("Click on Get Sales Orders to fetch sales orders based on the above filters.")
+ },
+ {
+ fieldname: "get_items",
+ title: "Get Finished Goods for Manufacture",
+ description: __("Click on 'Get Finished Goods for Manufacture' to fetch the items from the above Sales Orders. Items only for which a BOM is present will be fetched.")
+ },
+ {
+ fieldname: "po_items",
+ title: "Finished Goods",
+ description: __("On expanding a row in the Items to Manufacture table, you'll see an option to 'Include Exploded Items'. Ticking this includes raw materials of the sub-assembly items in the production process.")
+ },
+ {
+ fieldname: "include_non_stock_items",
+ title: "Include Non Stock Items",
+ description: __("To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked.")
+ },
+ {
+ fieldname: "include_subcontracted_items",
+ title: "Include Subcontracted Items",
+ description: __("To add subcontracted Item's raw materials if include exploded items is disabled.")
+ }
+];
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 8437895..56cf2b4 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -16,10 +16,12 @@
"customer",
"warehouse",
"project",
+ "sales_order_status",
"column_break2",
"from_date",
"to_date",
- "sales_order_status",
+ "from_delivery_date",
+ "to_delivery_date",
"sales_orders_detail",
"get_sales_orders",
"sales_orders",
@@ -300,7 +302,7 @@
{
"fieldname": "for_warehouse",
"fieldtype": "Link",
- "label": "Material Request Warehouse",
+ "label": "Make Material Request for Warehouse",
"options": "Warehouse"
},
{
@@ -358,13 +360,23 @@
"fieldname": "get_sub_assembly_items",
"fieldtype": "Button",
"label": "Get Sub Assembly Items"
+ },
+ {
+ "fieldname": "from_delivery_date",
+ "fieldtype": "Date",
+ "label": "From Delivery Date"
+ },
+ {
+ "fieldname": "to_delivery_date",
+ "fieldtype": "Date",
+ "label": "To Delivery Date"
}
],
"icon": "fa fa-calendar",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-06-28 20:00:33.905114",
+ "modified": "2021-09-06 18:35:59.642232",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 6a024f2..b9efe9b 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -3,18 +3,32 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json, copy
-from frappe import msgprint, _
+
+import copy
+import json
+
+import frappe
+from frappe import _, msgprint
+from frappe.model.document import Document
+from frappe.utils import (
+ add_days,
+ ceil,
+ cint,
+ comma_and,
+ flt,
+ get_link_to_form,
+ getdate,
+ now_datetime,
+ nowdate,
+)
+from frappe.utils.csvutils import build_csv_response
from six import iteritems
-from frappe.model.document import Document
-from frappe.utils import (flt, cint, nowdate, add_days, comma_and, now_datetime,
- ceil, get_link_to_form, getdate)
-from frappe.utils.csvutils import build_csv_response
-from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_children
+from erpnext.manufacturing.doctype.bom.bom import get_children, validate_bom_no
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
+
class ProductionPlan(Document):
def validate(self):
self.calculate_total_planned_qty()
@@ -109,6 +123,15 @@
so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)]
return so_mr_list
+ 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})
+ 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
+ return bom_item
+
def get_so_items(self):
# Check for empty table or empty rows
if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"):
@@ -117,16 +140,26 @@
so_list = self.get_so_mr_list("sales_order", "sales_orders")
item_condition = ""
- if self.item_code:
+ bom_item = "bom.item = so_item.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))
- items = frappe.db.sql("""select distinct parent, item_code, warehouse,
- (qty - work_order_qty) * conversion_factor as pending_qty, description, name
- from `tabSales Order Item` so_item
- where parent in (%s) and docstatus = 1 and qty > work_order_qty
- and exists (select name from `tabBOM` bom where bom.item=so_item.item_code
- and bom.is_active = 1) %s""" % \
- (", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
+ items = frappe.db.sql("""
+ select
+ distinct parent, item_code, warehouse,
+ (qty - work_order_qty) * conversion_factor as pending_qty,
+ description, name
+ from
+ `tabSales Order Item` so_item
+ 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)
if self.item_code:
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
@@ -194,7 +227,6 @@
})
pi = self.append('po_items', {
- 'include_exploded_items': 1,
'warehouse': data.warehouse,
'item_code': data.item_code,
'description': data.description or item_details.description,
@@ -205,6 +237,7 @@
'planned_start_date': now_datetime(),
'product_bundle_item': data.parent_item
})
+ pi._set_defaults()
if self.get_items_from == "Sales Order":
pi.sales_order = data.parent
@@ -278,7 +311,7 @@
if self.total_produced_qty > 0:
self.status = "In Process"
- if self.total_produced_qty == self.total_planned_qty:
+ if self.total_produced_qty >= self.total_planned_qty:
self.status = "Completed"
if self.status != 'Completed':
@@ -312,7 +345,7 @@
def get_production_items(self):
item_dict = {}
for d in self.po_items:
- item_details= {
+ item_details = {
"production_item" : d.item_code,
"use_multi_level_bom" : d.include_exploded_items,
"sales_order" : d.sales_order,
@@ -327,7 +360,7 @@
"production_plan" : self.name,
"production_plan_item" : d.name,
"product_bundle_item" : d.product_bundle_item,
- "make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0)
+ "planned_start_date" : d.planned_start_date
}
item_details.update({
@@ -424,7 +457,8 @@
def prepare_args_for_sub_assembly_items(self, row, args):
for field in ["production_item", "item_name", "qty", "fg_warehouse",
- "description", "bom_no", "stock_uom", "bom_level", "production_plan_item"]:
+ "description", "bom_no", "stock_uom", "bom_level",
+ "production_plan_item", "schedule_date"]:
args[field] = row.get(field)
args.update({
@@ -434,10 +468,14 @@
})
def create_work_order(self, item):
- from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError, get_default_warehouse
+ from erpnext.manufacturing.doctype.work_order.work_order import (
+ OverProductionError,
+ get_default_warehouse,
+ )
warehouse = get_default_warehouse()
wo = frappe.new_doc("Work Order")
wo.update(item)
+ wo.planned_start_date = item.get('planned_start_date') or item.get('schedule_date')
if item.get("warehouse"):
wo.fg_warehouse = item.get("warehouse")
@@ -524,8 +562,6 @@
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
- self.save()
-
def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
bom_data = sorted(bom_data, key = lambda i: i.bom_level)
@@ -549,7 +585,10 @@
'Reserved Qty for Production', 'Safety Stock', 'Required Qty']]
doc.warehouse = None
- for d in get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True):
+ frappe.flags.show_qty_in_stock_uom = 1
+ 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('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')])
@@ -585,9 +624,16 @@
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):
- item_details.setdefault(d.get('item_code'), d)
+ 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)
+
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("""
@@ -622,6 +668,9 @@
if d.item_code in item_details:
item_details[d.item_code].qty = item_details[d.item_code].qty + d.qty
else:
+ if not d.conversion_factor and d.purchase_uom:
+ d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom)
+
item_details[d.item_code] = d
if data.get('include_exploded_items') and d.default_bom:
@@ -649,10 +698,11 @@
row['purchase_uom'] = row['stock_uom']
if row['purchase_uom'] != row['stock_uom']:
- if not row['conversion_factor']:
+ 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"):
required_qty = ceil(required_qty)
@@ -683,42 +733,43 @@
def get_sales_orders(self):
so_filter = item_filter = ""
- if self.from_date:
- so_filter += " and so.transaction_date >= %(from_date)s"
- if self.to_date:
- so_filter += " and so.transaction_date <= %(to_date)s"
- if self.customer:
- so_filter += " and so.customer = %(customer)s"
- if self.project:
- so_filter += " and so.project = %(project)s"
- if self.sales_order_status:
- so_filter += "and so.status = %(sales_order_status)s"
+ bom_item = "bom.item = so_item.item_code"
- if self.item_code:
- item_filter += " and so_item.item_code = %(item)s"
+ 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')
+ }
- open_so = frappe.db.sql("""
+ 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']:
+ if self.get(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):
+ 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"""
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.company = %(company)s
- and so_item.qty > so_item.work_order_qty {0} {1}
- and (exists (select name from `tabBOM` bom where bom.item=so_item.item_code
+ and so_item.qty > so_item.work_order_qty {so_filter} {item_filter}
+ and (exists (select name from `tabBOM` bom where {bom_item}
and bom.is_active = 1)
or exists (select name from `tabPacked Item` pi
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)))
- """.format(so_filter, item_filter), {
- "from_date": self.from_date,
- "to_date": self.to_date,
- "customer": self.customer,
- "project": self.project,
- "item": self.item_code,
- "company": self.company,
- "sales_order_status": self.sales_order_status
- }, as_dict=1)
+ """, self.as_dict(), as_dict=1)
+
return open_so
@frappe.whitelist()
@@ -747,6 +798,12 @@
group by item_code, warehouse
""".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
+ )
+
def get_warehouse_list(warehouses):
warehouse_list = []
@@ -819,10 +876,8 @@
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 = 0
- for d in item_master.get("uoms"):
- if d.uom == purchase_uom:
- conversion_factor = d.conversion_factor
+ 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(
{
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
index ca597f6..b4bc346 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'production_plan',
@@ -14,4 +16,4 @@
'items': ['Purchase Order']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
index c2e3e6d..8f94686 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
@@ -1,4 +1,5 @@
frappe.listview_settings['Production Plan'] = {
+ hide_name_column: true,
add_fields: ["status"],
filters: [["status", "!=", "Closed"]],
get_indicator: function (doc) {
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.js b/erpnext/manufacturing/doctype/production_plan/test_production_plan.js
deleted file mode 100644
index ef7d64c..0000000
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Production Plan", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Production Plan
- () => frappe.tests.make('Production Plan', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 93e6d7a..707b3f6 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -3,14 +3,23 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import nowdate, now_datetime, flt
-from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders
-from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
+
+import frappe
+from frappe.utils import add_to_date, flt, now_datetime, nowdate
+
+from erpnext.controllers.item_variant import create_variant
+from erpnext.manufacturing.doctype.production_plan.production_plan import (
+ get_items_for_material_requests,
+ get_sales_orders,
+ get_warehouse_list,
+)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list
+from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+ create_stock_reconciliation,
+)
+
class TestProductionPlan(unittest.TestCase):
def setUp(self):
@@ -60,6 +69,21 @@
pln = frappe.get_doc('Production Plan', pln.name)
pln.cancel()
+ def test_production_plan_start_date(self):
+ planned_date = add_to_date(date=None, days=3)
+ plan = create_production_plan(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})
+
+ self.assertEqual(work_orders[0].planned_start_date, planned_date)
+
+ for wo in work_orders:
+ frappe.delete_doc('Work Order', wo.name)
+
+ frappe.get_doc('Production Plan', plan.name).cancel()
+
def test_production_plan_for_existing_ordered_qty(self):
sr1 = create_stock_reconciliation(item_code="Raw Material Item 1",
target="_Test Warehouse - _TC", qty=1, rate=110)
@@ -271,6 +295,61 @@
self.assertEqual(warehouses, expected_warehouses)
+ def test_get_sales_order_with_variant(self):
+ 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"
+ },
+ ],
+ "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])
+ else:
+ parent_bom = frappe.get_doc('BOM', {"item": 'PIV'})
+
+ 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])
+ 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])
+
+ """Testing when item variant has a BOM"""
+ so = make_sales_order(item_code="PIV-RED", qty=5)
+ pln = frappe.new_doc('Production Plan')
+ pln.company = so.company
+ 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].bom_no, variant_bom.name)
+ so.cancel()
+ frappe.delete_doc('Sales Order', so.name)
+ variant_bom.cancel()
+ 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)
+ pln.get_open_sales_orders()
+ 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].bom_no, parent_bom.name)
+
+ frappe.db.rollback()
def create_production_plan(**args):
args = frappe._dict(args)
@@ -325,6 +404,7 @@
'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:
diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py
index 8b57042..24029d8 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py
+++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ProductionPlanItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py
index 51fbc36..9d25d6f 100644
--- a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py
+++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProductionPlanItemReference(Document):
pass
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.py b/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.py
index 44786f8..d1d935c 100644
--- a/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.py
+++ b/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProductionPlanMaterialRequest(Document):
pass
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py
index f605985..1548bd7 100644
--- a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProductionPlanMaterialRequestWarehouse(Document):
pass
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
index ecab5fb..905252d 100644
--- a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestProductionPlanMaterialRequestWarehouse(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py b/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py
index ef7f79e..ea53a98 100644
--- a/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py
+++ b/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ProductionPlanSalesOrder(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py
index 6850a2e..be0ed1b 100644
--- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProductionPlanSubAssemblyItem(Document):
pass
diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js
index 032c9cd..33a313e 100644
--- a/erpnext/manufacturing/doctype/routing/routing.js
+++ b/erpnext/manufacturing/doctype/routing/routing.js
@@ -69,3 +69,16 @@
frm.events.calculate_operating_cost(frm, d);
}
});
+
+frappe.tour['Routing'] = [
+ {
+ fieldname: "routing_name",
+ title: "Routing Name",
+ description: __("Enter a name for Routing.")
+ },
+ {
+ fieldname: "operations",
+ title: "BOM Operations",
+ description: __("Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically.\n\n After that, set the Operation Time in minutes and the table will calculate the Operation Costs based on the Hourly Rate and Operation Time.")
+ }
+];
diff --git a/erpnext/manufacturing/doctype/routing/routing.py b/erpnext/manufacturing/doctype/routing/routing.py
index ece0db7..20fb370 100644
--- a/erpnext/manufacturing/doctype/routing/routing.py
+++ b/erpnext/manufacturing/doctype/routing/routing.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cint, flt
from frappe import _
from frappe.model.document import Document
+from frappe.utils import cint, flt
+
class Routing(Document):
def validate(self):
diff --git a/erpnext/manufacturing/doctype/routing/routing_dashboard.py b/erpnext/manufacturing/doctype/routing/routing_dashboard.py
index ab309cc..9ef6ee5 100644
--- a/erpnext/manufacturing/doctype/routing/routing_dashboard.py
+++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['BOM']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.js b/erpnext/manufacturing/doctype/routing/test_routing.js
deleted file mode 100644
index 6cb6549..0000000
--- a/erpnext/manufacturing/doctype/routing/test_routing.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Routing", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Routing
- () => frappe.tests.make('Routing', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py
index 92f2694..b84b2ba 100644
--- a/erpnext/manufacturing/doctype/routing/test_routing.py
+++ b/erpnext/manufacturing/doctype/routing/test_routing.py
@@ -4,11 +4,14 @@
from __future__ import unicode_literals
import unittest
+
import frappe
from frappe.test_runner import make_test_records
-from erpnext.stock.doctype.item.test_item import make_item
+
from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
+from erpnext.stock.doctype.item.test_item import make_item
+
class TestRouting(unittest.TestCase):
@classmethod
@@ -91,8 +94,8 @@
def setup_operations(rows):
- from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
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)
diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.py b/erpnext/manufacturing/doctype/sub_operation/sub_operation.py
index f4b2775..37b64f2 100644
--- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.py
+++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class SubOperation(Document):
pass
diff --git a/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py b/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
index d3410ca..c5749db 100644
--- a/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
+++ b/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestSubOperation(unittest.TestCase):
pass
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index bf1ccb7..85b5bfb 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1,20 +1,26 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-
-
-from __future__ import unicode_literals
import unittest
+
import frappe
-from frappe.utils import flt, now, add_months, cint, today, add_to_date
-from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
- ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError)
-from erpnext.stock.doctype.stock_entry import test_stock_entry
-from erpnext.stock.utils import get_bin
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from frappe.utils import add_months, cint, flt, now, today
+
from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
+from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+from erpnext.manufacturing.doctype.work_order.work_order import (
+ CapacityError,
+ ItemHasVariantError,
+ OverProductionError,
+ StockOverProductionError,
+ make_stock_entry,
+ stop_unstop,
+)
+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.stock_entry import test_stock_entry
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.stock.utils import get_bin
+
class TestWorkOrder(unittest.TestCase):
def setUp(self):
@@ -675,13 +681,18 @@
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,
+ })
if not frappe.db.get_value('BOM', {'item': item_name}):
- make_bom(item=item_name, raw_materials=[item_name], rm_qty=1)
+ 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)
@@ -690,6 +701,127 @@
self.assertRaises(frappe.ValidationError, make_stock_entry, wo.name, 'Material Transfer for Manufacture')
+ def test_wo_completion_with_pl_bom(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import (
+ create_bom_with_process_loss_item,
+ create_process_loss_bom_items,
+ )
+
+ qty = 4
+ 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)
+
+ 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
+ )
+ bom_doc.submit()
+
+ wo = make_wo_order_test_record(
+ production_item=fg_item_non_whole.item_code,
+ bom_no=bom_no,
+ wip_warehouse=wip_warehouse,
+ qty=qty,
+ skip_transfer=1,
+ stock_uom=fg_item_non_whole.stock_uom,
+ )
+
+ 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.insert()
+ se.submit()
+
+ # Testing stock entry values
+ items = se.get("items")
+ self.assertEqual(len(items), 3, "There should be 3 items including process loss.")
+
+ source_item, fg_item, pl_item = items
+
+ total_pl_qty = qty * scrap_qty
+ actual_fg_qty = qty - total_pl_qty
+
+ self.assertEqual(pl_item.qty, total_pl_qty)
+ 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
+ )
+
+ 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']
+
+ 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')
+
+ 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.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')
+ update_job_card(job_card)
+
+ stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
+ for row in stock_entry.items:
+ if row.is_scrap_item:
+ self.assertEqual(row.qty, 1)
+
+def update_job_card(job_card):
+ 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
+ },
+ ])
+
+ job_card_doc.append('time_logs', {
+ 'from_time': now(),
+ '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`
@@ -732,6 +864,7 @@
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()
+ wo_order.transfer_material_against = args.transfer_material_against or "Work Order"
if args.source_warehouse:
for item in wo_order.get("required_items"):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 5120485..51c46f6 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -731,3 +731,63 @@
});
}
};
+
+frappe.tour['Work Order'] = [
+ {
+ fieldname: "production_item",
+ title: "Item to Manufacture",
+ description: __("Select the Item to be manufactured.")
+ },
+ {
+ fieldname: "bom_no",
+ title: "BOM No",
+ description: __("The default BOM for that item will be fetched by the system. You can also change the BOM.")
+ },
+ {
+ fieldname: "qty",
+ title: "Qty to Manufacture",
+ description: __("Enter the quantity to manufacture. Raw material Items will be fetched only when this is set.")
+ },
+ {
+ fieldname: "use_multi_level_bom",
+ title: "Use Multi-Level BOM",
+ description: __("This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox.")
+ },
+ {
+ fieldname: "source_warehouse",
+ title: "Source Warehouse",
+ description: __("The warehouse where you store your raw materials. Each required item can have a separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of the Work Order, the raw materials will be reserved in these warehouses for production usage.")
+ },
+ {
+ fieldname: "fg_warehouse",
+ title: "Target Warehouse",
+ description: __("The warehouse where you store finished Items before they are shipped.")
+ },
+ {
+ fieldname: "wip_warehouse",
+ title: "Work-in-Progress Warehouse",
+ description: __("The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse.")
+ },
+ {
+ fieldname: "scrap_warehouse",
+ title: "Scrap Warehouse",
+ description: __("If the BOM results in Scrap material, the Scrap Warehouse needs to be selected.")
+ },
+ {
+ fieldname: "required_items",
+ title: "Required Items",
+ description: __("All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table.")
+ },
+ {
+ fieldname: "planned_start_date",
+ title: "Planned Start Date",
+ description: __("Set the Planned Start Date (an Estimated Date at which you want the Production to begin)")
+ },
+ {
+ fieldname: "operations",
+ title: "Operations",
+ description: __("If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed.")
+ },
+
+
+];
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 3b56854..913fc85 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -19,6 +19,7 @@
"qty",
"material_transferred_for_manufacturing",
"produced_qty",
+ "process_loss_qty",
"sales_order",
"project",
"serial_no_and_batch_for_finished_good_section",
@@ -64,16 +65,12 @@
"description",
"stock_uom",
"column_break2",
- "references_section",
"material_request",
"material_request_item",
"sales_order_item",
- "column_break_61",
"production_plan",
"production_plan_item",
"production_plan_sub_assembly_item",
- "parent_work_order",
- "bom_level",
"product_bundle_item",
"amended_from"
],
@@ -553,20 +550,29 @@
"read_only": 1
},
{
- "fieldname": "production_plan_sub_assembly_item",
- "fieldtype": "Data",
- "label": "Production Plan Sub-assembly Item",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- }
+ "fieldname": "production_plan_sub_assembly_item",
+ "fieldtype": "Data",
+ "label": "Production Plan Sub-assembly Item",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.process_loss_qty",
+ "fieldname": "process_loss_qty",
+ "fieldtype": "Float",
+ "label": "Process Loss Qty",
+ "no_copy": 1,
+ "non_negative": 1,
+ "read_only": 1
+ }
],
"icon": "fa fa-cogs",
"idx": 1,
"image_field": "image",
"is_submittable": 1,
"links": [],
- "modified": "2021-06-28 16:19:14.902699",
+ "modified": "2021-08-24 15:14:03.844937",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 282b5d0..e282dd3 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1,25 +1,43 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-import frappe
import json
-import math
-from frappe import _
-from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form, time_diff_in_hours
-from frappe.model.document import Document
-from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict, get_bom_item_rate
+
+import frappe
from dateutil.relativedelta import relativedelta
-from erpnext.stock.doctype.item.item import validate_end_of_life, get_item_defaults
-from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
-from erpnext.projects.doctype.timesheet.timesheet import OverlapError
-from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
-from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
-from frappe.utils.csvutils import getlink
-from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
-from erpnext.utilities.transaction_base import validate_uom_is_integer
+from frappe import _
+from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
+from frappe.utils import (
+ cint,
+ date_diff,
+ flt,
+ get_datetime,
+ get_link_to_form,
+ getdate,
+ nowdate,
+ time_diff_in_hours,
+)
+
+from erpnext.manufacturing.doctype.bom.bom import (
+ get_bom_item_rate,
+ get_bom_items_as_dict,
+ validate_bom_no,
+)
+from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import (
+ get_mins_between_operations,
+)
from erpnext.stock.doctype.batch.batch import make_batch
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_auto_serial_nos, auto_make_serial_nos
+from erpnext.stock.doctype.item.item import get_item_defaults, validate_end_of_life
+from erpnext.stock.doctype.serial_no.serial_no import (
+ auto_make_serial_nos,
+ get_auto_serial_nos,
+ get_serial_nos,
+)
+from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
+from erpnext.stock.utils import get_bin, get_latest_stock_qty, validate_warehouse_company
+from erpnext.utilities.transaction_base import validate_uom_is_integer
+
class OverProductionError(frappe.ValidationError): pass
class CapacityError(frappe.ValidationError): pass
@@ -214,6 +232,7 @@
self.meta.get_label(fieldname), qty, completed_qty, self.name), StockOverProductionError)
self.db_set(fieldname, qty)
+ self.set_process_loss_qty()
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
@@ -223,6 +242,22 @@
if self.production_plan:
self.update_production_plan_status()
+ def set_process_loss_qty(self):
+ process_loss_qty = flt(frappe.db.sql("""
+ SELECT sum(qty) FROM `tabStock Entry Detail`
+ WHERE
+ is_process_loss=1
+ AND parent IN (
+ SELECT name FROM `tabStock Entry`
+ WHERE
+ work_order=%s
+ AND purpose='Manufacture'
+ AND docstatus=1
+ )
+ """, (self.name, ))[0][0])
+ if process_loss_qty is not None:
+ 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)
produced_qty = 0
@@ -602,7 +637,7 @@
if self.docstatus==1:
# calculate transferred qty based on submitted stock entries
- self.update_transaferred_qty_for_required_items()
+ self.update_transferred_qty_for_required_items()
# update in bin
self.update_reserved_qty_for_production()
@@ -671,7 +706,7 @@
self.set_available_qty()
- def update_transaferred_qty_for_required_items(self):
+ def update_transferred_qty_for_required_items(self):
'''update transferred qty from submitted stock entries for that item against
the work order'''
@@ -838,7 +873,7 @@
for item in variant_items:
args = frappe._dict({
- "item_code": item.get("varint_item_code"),
+ "item_code": item.get("variant_item_code"),
"required_qty": item.get("qty"),
"qty": item.get("qty"), # for bom
"source_warehouse": item.get("source_warehouse"),
@@ -859,7 +894,7 @@
}, bom_doc)
if not args.source_warehouse:
- args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"),
+ 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"))
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
index 9aa0715..f0fc43f 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'work_order',
@@ -17,4 +19,4 @@
'items': ['Serial No', 'Batch']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_preview.html b/erpnext/manufacturing/doctype/work_order/work_order_preview.html
index a4bf93e..95bdd29 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order_preview.html
+++ b/erpnext/manufacturing/doctype/work_order/work_order_preview.html
@@ -30,4 +30,4 @@
</p>
</div>
</div>
-</div>
\ No newline at end of file
+</div>
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 d18f028..3f2664b 100644
--- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
+++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class WorkOrderItem(Document):
pass
def on_doctype_update():
- frappe.db.add_index("Work Order Item", ["item_code", "source_warehouse"])
\ No newline at end of file
+ frappe.db.add_index("Work Order Item", ["item_code", "source_warehouse"])
diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py
index 3c20d8e..d25c9f2 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class WorkOrderOperation(Document):
pass
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index 9b73aca..6c6ab77 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -1,14 +1,20 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
from __future__ import unicode_literals
-from erpnext.manufacturing.doctype.operation.test_operation import make_operation
+
+import unittest
import frappe
-import unittest
-from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours, NotInWorkingHoursError, WorkstationHolidayError
-from erpnext.manufacturing.doctype.routing.test_routing import setup_bom, create_routing
from frappe.test_runner import make_test_records
+from erpnext.manufacturing.doctype.operation.test_operation import make_operation
+from erpnext.manufacturing.doctype.routing.test_routing import create_routing, setup_bom
+from erpnext.manufacturing.doctype.workstation.workstation import (
+ NotInWorkingHoursError,
+ WorkstationHolidayError,
+ check_if_within_operating_hours,
+)
+
test_dependencies = ["Warehouse"]
test_records = frappe.get_test_records('Workstation')
make_test_records('Workstation')
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js
index ba8e30c..5b9cedb 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.js
+++ b/erpnext/manufacturing/doctype/workstation/workstation.js
@@ -16,4 +16,29 @@
})
}
}
-})
\ No newline at end of file
+});
+
+frappe.tour['Workstation'] = [
+ {
+ fieldname: "workstation_name",
+ title: "Workstation Name",
+ description: __("You can set it as a machine name or operation type. For example, stiching machine 12")
+ },
+ {
+ fieldname: "production_capacity",
+ title: "Production Capacity",
+ description: __("No. of parallel job cards which can be allowed on this workstation. Example: 2 would mean this workstation can process production for two Work Orders at a time.")
+ },
+ {
+ fieldname: "holiday_list",
+ title: "Holiday List",
+ description: __("A Holiday List can be added to exclude counting these days for the Workstation.")
+ },
+ {
+ fieldname: "working_hours",
+ title: "Working Hours",
+ description: __("Under Working Hours table, you can add start and end times for a Workstation. For example, a Workstation may be active from 9 am to 1 pm, then 2 pm to 5 pm. You can also specify the working hours based on shifts. While scheduling a Work Order, the system will check for the availability of the Workstation based on the working hours specified.")
+ },
+
+
+];
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index f4483f7..6daf950 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -2,13 +2,23 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from erpnext.support.doctype.issue.issue import get_holidays
-from frappe.utils import (flt, cint, getdate, formatdate,
- comma_and, time_diff_in_seconds, to_timedelta, add_days)
from frappe.model.document import Document
-from dateutil.parser import parse
+from frappe.utils import (
+ add_days,
+ cint,
+ comma_and,
+ flt,
+ formatdate,
+ getdate,
+ time_diff_in_seconds,
+ to_timedelta,
+)
+
+from erpnext.support.doctype.issue.issue import get_holidays
+
class WorkstationHolidayError(frappe.ValidationError): pass
class NotInWorkingHoursError(frappe.ValidationError): pass
diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
index 3ddbe73..3e4b38a 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'workstation',
diff --git a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.py b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.py
index 215df4c..719d83d 100644
--- a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.py
+++ b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class WorkstationWorkingHour(Document):
pass
diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
index 7317152..032091f 100644
--- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
@@ -19,14 +19,14 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing",
"idx": 0,
"is_complete": 0,
- "modified": "2020-06-29 20:25:36.899106",
+ "modified": "2021-08-13 16:04:34.333812",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing",
"owner": "Administrator",
"steps": [
{
- "step": "Warehouse"
+ "step": "Explore Manufacturing Settings"
},
{
"step": "Workstation"
@@ -35,22 +35,22 @@
"step": "Operation"
},
{
- "step": "Create Product"
+ "step": "Routing"
},
{
- "step": "Create Raw Materials"
+ "step": "Create Product"
},
{
"step": "Create BOM"
},
{
- "step": "Work Order"
+ "step": "Production Planning"
},
{
- "step": "Explore Manufacturing Settings"
+ "step": "Work Order"
}
],
"subtitle": "Products, Raw Materials, BOM, Work Order, and more.",
"success_message": "Manufacturing module is all set up!",
"title": "Let's Set Up the Manufacturing Module."
-}
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.py b/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.py
index 2334f8b..f57de91 100644
--- a/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.py
+++ b/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json
index 84b4088..9d7a58c 100644
--- a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json
+++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json
@@ -1,19 +1,21 @@
{
"action": "Create Entry",
+ "action_label": "Create your first Bill of Materials",
"creation": "2020-05-05 16:41:20.239696",
+ "description": "# Create a Bill of Materials\n\nA Bill of Materials (BOM) is a list of items and sub-assemblies with quantities required to manufacture an Item.\n\nBOM also provides cost estimation for the production of the item. It takes raw-materials cost based on valuation and operations to cost based on routing, which gives total costing for a BOM.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-19 12:51:31.315686",
+ "modified": "2021-08-13 16:00:51.092671",
"modified_by": "Administrator",
"name": "Create BOM",
"owner": "Administrator",
"reference_document": "BOM",
+ "show_form_tour": 1,
"show_full_form": 1,
- "title": "Create a BOM (Bill of Material)",
+ "title": "Bill of Materials",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json
index 0ffa301..ad73452 100644
--- a/erpnext/manufacturing/onboarding_step/create_product/create_product.json
+++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json
@@ -1,19 +1,21 @@
{
"action": "Create Entry",
+ "action_label": "Create an Item",
"creation": "2020-05-05 16:42:31.476275",
+ "description": "# Create Items for Bill of Materials\n\nOne of the prerequisites of a BOM is the creation of raw materials, sub-assembly, and finished items. Once these items are created, you will be able to proceed to the Bill of Materials master, which is composed of items and routing.\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-19 12:50:59.010439",
+ "modified": "2021-08-13 16:00:22.407811",
"modified_by": "Administrator",
"name": "Create Product",
"owner": "Administrator",
"reference_document": "Item",
- "show_full_form": 0,
- "title": "Create a Finished Good",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Finished Items",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json
index 0764f2e..3f94764 100644
--- a/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json
+++ b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json
@@ -5,7 +5,6 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-19 11:53:25.147837",
@@ -13,6 +12,7 @@
"name": "Create Raw Materials",
"owner": "Administrator",
"reference_document": "Item",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create Raw Materials",
"validate_action": 1
diff --git a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json
index 7ef202e..1d2c27e 100644
--- a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json
+++ b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json
@@ -1,20 +1,22 @@
{
"action": "Show Form Tour",
+ "action_label": "Take a walk-through of Manufacturing Settings",
"creation": "2020-05-19 11:55:11.378374",
+ "description": "# Review Manufacturing Settings\n\nIn ERPNext, the Manufacturing module\u2019s features are configurable as per your business needs. Manufacturing Settings is the place where you can set your preferences for:\n\n- Capacity planning for allocating jobs to workstations\n- Raw-material consumption based on BOM or actual\n- Default values and over-production allowance\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
- "modified": "2020-05-26 20:28:03.558199",
+ "modified": "2021-08-13 15:59:32.145655",
"modified_by": "Administrator",
"name": "Explore Manufacturing Settings",
"owner": "Administrator",
"reference_document": "Manufacturing Settings",
+ "show_form_tour": 0,
"show_full_form": 0,
- "title": "Explore Manufacturing Settings",
+ "title": "Manufacturing Settings",
"validate_action": 0,
"video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4"
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json
index b532e67..2e95921 100644
--- a/erpnext/manufacturing/onboarding_step/operation/operation.json
+++ b/erpnext/manufacturing/onboarding_step/operation/operation.json
@@ -1,19 +1,21 @@
{
"action": "Create Entry",
+ "action_label": "Let\u2019s create an Operation",
"creation": "2020-05-12 16:15:31.706756",
+ "description": "# Create Operations\n\nAn Operation refers to any manufacturing operation performed on the raw materials to process it further in the manufacturing path. As an example, if you are into garments manufacturing, you will create Operations like fabric cutting, stitching, and washing as some of the operations.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-19 12:50:41.642754",
+ "modified": "2021-08-13 15:59:53.313532",
"modified_by": "Administrator",
"name": "Operation",
"owner": "Administrator",
"reference_document": "Operation",
- "show_full_form": 0,
- "title": "Create a Operation",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Operation",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/production_planning/production_planning.json b/erpnext/manufacturing/onboarding_step/production_planning/production_planning.json
new file mode 100644
index 0000000..ff9fdb9
--- /dev/null
+++ b/erpnext/manufacturing/onboarding_step/production_planning/production_planning.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Learn more about Production Planning",
+ "creation": "2021-08-04 17:33:06.551077",
+ "description": "# How Production Planning Works\n\nProduction Plan helps in production and material planning for the Items planned for manufacturing. These production items can be committed via Sales Order (to Customers) or Material Requests (internally).\n",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 16:04:15.491414",
+ "modified_by": "Administrator",
+ "name": "Production Planning",
+ "owner": "Administrator",
+ "reference_document": "Production Plan",
+ "show_form_tour": 0,
+ "show_full_form": 1,
+ "title": "Production Planning",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/routing/routing.json b/erpnext/manufacturing/onboarding_step/routing/routing.json
new file mode 100644
index 0000000..be4fbff
--- /dev/null
+++ b/erpnext/manufacturing/onboarding_step/routing/routing.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Check help to setup Routing",
+ "creation": "2021-08-04 11:56:42.455758",
+ "description": "# Setup Routing\n\nA Routing stores all Operations along with the description, hourly rate, operation time, batch size, etc. Click below to learn how the Routing template can be created, for quick selection in the BOM.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-13 16:00:07.391563",
+ "modified_by": "Administrator",
+ "name": "Routing",
+ "owner": "Administrator",
+ "reference_document": "Routing",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Routing",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json
index c63363e..d2756c9 100644
--- a/erpnext/manufacturing/onboarding_step/work_order/work_order.json
+++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json
@@ -1,19 +1,21 @@
{
"action": "Create Entry",
+ "action_label": "Create your first Work Order",
"creation": "2020-05-12 16:15:56.084682",
+ "description": "# Create a Work Order\n\nA Work Order or a Job order is given to the manufacturing shop floor by the Production Manager to initiate the manufacturing of a certain quantity of an item. Work Order carriers details of production Item, its BOM, quantities to be manufactured, and operations.\n\nThrough Work Order, you can track various production status like:\n\n- Issue of raw-material to shop material\n- Progress on each Workstation via Job Card\n- Manufactured Quantity against Work Order\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-19 12:51:38.133150",
+ "modified": "2021-08-13 16:02:04.223536",
"modified_by": "Administrator",
"name": "Work Order",
"owner": "Administrator",
"reference_document": "Work Order",
+ "show_form_tour": 1,
"show_full_form": 1,
- "title": "Create a Work Order",
+ "title": "Work Order",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json
index df244bb..67dd52e 100644
--- a/erpnext/manufacturing/onboarding_step/workstation/workstation.json
+++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json
@@ -1,19 +1,21 @@
{
"action": "Create Entry",
+ "action_label": "Let\u2019s create a Workstation",
"creation": "2020-05-12 16:14:14.930214",
+ "description": "# Create Workstations\n\nA Workstation stores information regarding the place where the workstation operations are performed. As an example, if you have ten sewing machines doing stitching jobs, each machine will be added as a workstation.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-19 12:50:33.938176",
+ "modified": "2021-08-13 15:59:59.634802",
"modified_by": "Administrator",
"name": "Workstation",
"owner": "Administrator",
"reference_document": "Workstation",
- "show_full_form": 0,
- "title": "Create a Workstation / Machine",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Workstation",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
index 858b554..c122fa9 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from pprint import pprint
+
def execute(filters=None):
data = []
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 e7d9265..3c2d215 100644
--- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
+++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
data = get_data(filters)
columns = get_columns(filters)
@@ -108,5 +110,3 @@
"fieldtype": "Int",
"width": 180
}]
-
-
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 ffd9242..c085990 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -2,12 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils.data import comma_and
+
def execute(filters=None):
-# if not filters: filters = {}
+ # if not filters: filters = {}
columns = get_columns()
summ_data = []
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html
index 119a4fc..2ae8848 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html
@@ -24,4 +24,4 @@
</tr>
{% } %}
</tbody>
-</table>
\ No newline at end of file
+</table>
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 ed8b939..b8ef68d 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if not filters: filters = {}
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 dc424b7..bbf5037 100644
--- a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
+++ b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns, data = [], []
columns = get_columns(filters)
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 9f81e7d..0dcad44 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
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
@@ -124,4 +126,4 @@
"fieldname": "total_time_in_mins",
"width": "100"
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
index 093309a..a1c6fd1 100644
--- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
+++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt
from frappe import _
+from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
@@ -110,4 +112,4 @@
"fieldtype": "Text",
"width": 100
}
- ]
\ No newline at end of file
+ ]
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 fc27d35..f014e7f 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -2,12 +2,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import flt, nowdate, add_years, cint, getdate
+from frappe.utils import add_years, cint, flt, getdate
+
+import erpnext
from erpnext.accounts.report.financial_statements import get_period_list
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
+
def execute(filters=None):
return ForecastingReport(filters).execute_report()
@@ -239,4 +243,4 @@
"currency": self.company_currency,
"datatype": self.fieldtype
}
- ]
\ No newline at end of file
+ ]
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 b1bff35..a7aec31 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate, flt
-from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period)
+from frappe.utils import getdate
+
+from erpnext.stock.report.stock_analytics.stock_analytics import get_period, get_period_date_ranges
+
def execute(filters=None):
columns, data = [], []
@@ -201,4 +204,4 @@
}
])
- return columns
\ No newline at end of file
+ return columns
diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py
index 79af8a1..9e0978a 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.py
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, scrub
from frappe.utils import getdate
-from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period)
+
+from erpnext.stock.report.stock_analytics.stock_analytics import get_period, get_period_date_ranges
+
def execute(filters=None):
columns = get_columns(filters)
@@ -139,7 +142,3 @@
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 81b1791..9a4d0c4 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
data = get_data(filters)
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 806d268..e27270a 100644
--- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
+++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
# and bom_no is not null and bom_no !=''
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 6192632..54df208 100644
--- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
+++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns, data = [], []
data = get_data(filters)
@@ -129,4 +131,4 @@
}
])
- return columns
\ No newline at end of file
+ return columns
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 97553e6..5b2e262 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
@@ -2,18 +2,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.utils import cint
+
import frappe
+from frappe.utils import cint
+
def execute(filters=None):
wo_list = get_work_orders()
data = get_item_list(wo_list, filters)
columns = get_columns()
return columns, data
-
+
def get_item_list(wo_list, filters):
out = []
-
+
#Add a row for each item/qty
for wo_details in wo_list:
desc = frappe.db.get_value("BOM", wo_details.bom_no, "description")
@@ -70,13 +72,13 @@
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')
return out
-
+
def get_columns():
columns = [{
"fieldname": "work_order",
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 612dad0..b65af33 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import date_diff, today, getdate, flt
from frappe import _
-from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period)
+from frappe.utils import date_diff, flt, getdate, today
+
+from erpnext.stock.report.stock_analytics.stock_analytics import get_period, get_period_date_ranges
+
def execute(filters=None):
columns, data = [], []
@@ -265,4 +268,4 @@
},
])
- return columns
\ No newline at end of file
+ return columns
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index 62f5dce..a9f94ce 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -15,7 +15,6 @@
Maintenance
Education
Regional
-Healthcare
Restaurant
Agriculture
ERPNext Integrations
@@ -26,4 +25,4 @@
Communication
Loan Management
Payroll
-Telephony
\ No newline at end of file
+Telephony
diff --git a/erpnext/non_profit/doctype/certification_application/certification_application.py b/erpnext/non_profit/doctype/certification_application/certification_application.py
index d4fc76b..ff400c8 100644
--- a/erpnext/non_profit/doctype/certification_application/certification_application.py
+++ b/erpnext/non_profit/doctype/certification_application/certification_application.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CertificationApplication(Document):
pass
diff --git a/erpnext/non_profit/doctype/certification_application/test_certification_application.js b/erpnext/non_profit/doctype/certification_application/test_certification_application.js
deleted file mode 100644
index 40e9486..0000000
--- a/erpnext/non_profit/doctype/certification_application/test_certification_application.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Certification Application", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Certification Application
- () => frappe.tests.make('Certification Application', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/certification_application/test_certification_application.py b/erpnext/non_profit/doctype/certification_application/test_certification_application.py
index 30cb8c0..5e1cbf8 100644
--- a/erpnext/non_profit/doctype/certification_application/test_certification_application.py
+++ b/erpnext/non_profit/doctype/certification_application/test_certification_application.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCertificationApplication(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/certified_consultant/certified_consultant.py b/erpnext/non_profit/doctype/certified_consultant/certified_consultant.py
index 3bc6ed7..0cbc208 100644
--- a/erpnext/non_profit/doctype/certified_consultant/certified_consultant.py
+++ b/erpnext/non_profit/doctype/certified_consultant/certified_consultant.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CertifiedConsultant(Document):
pass
diff --git a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.js b/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.js
deleted file mode 100644
index f6a72a4..0000000
--- a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Certified Consultant", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Certified Consultant
- () => frappe.tests.make('Certified Consultant', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.py b/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.py
index 19b485d..29a7388 100644
--- a/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.py
+++ b/erpnext/non_profit/doctype/certified_consultant/test_certified_consultant.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCertifiedConsultant(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/chapter/chapter.py b/erpnext/non_profit/doctype/chapter/chapter.py
index e9554b1..c5c9569 100644
--- a/erpnext/non_profit/doctype/chapter/chapter.py
+++ b/erpnext/non_profit/doctype/chapter/chapter.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.website.website_generator import WebsiteGenerator
+
class Chapter(WebsiteGenerator):
_website = frappe._dict(
condition_field = "published",
diff --git a/erpnext/non_profit/doctype/chapter/test_chapter.js b/erpnext/non_profit/doctype/chapter/test_chapter.js
deleted file mode 100644
index e30d6a5..0000000
--- a/erpnext/non_profit/doctype/chapter/test_chapter.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Chapter", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Chapter
- () => frappe.tests.make('Chapter', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/chapter/test_chapter.py b/erpnext/non_profit/doctype/chapter/test_chapter.py
index d757a1f..04cdc27 100644
--- a/erpnext/non_profit/doctype/chapter/test_chapter.py
+++ b/erpnext/non_profit/doctype/chapter/test_chapter.py
@@ -5,5 +5,6 @@
import unittest
+
class TestChapter(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/chapter_member/chapter_member.py b/erpnext/non_profit/doctype/chapter_member/chapter_member.py
index c4b8999..1638294 100644
--- a/erpnext/non_profit/doctype/chapter_member/chapter_member.py
+++ b/erpnext/non_profit/doctype/chapter_member/chapter_member.py
@@ -3,9 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class ChapterMember(Document):
pass
-
-
diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py
index 4fd1a30..efbe496 100644
--- a/erpnext/non_profit/doctype/donation/donation.py
+++ b/erpnext/non_profit/doctype/donation/donation.py
@@ -3,15 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+import json
+
import frappe
import six
-import json
-from frappe.model.document import Document
from frappe import _
-from frappe.utils import getdate, flt, get_link_to_form
from frappe.email import sendmail_to_system_managers
+from frappe.model.document import Document
+from frappe.utils import flt, get_link_to_form, getdate
+
from erpnext.non_profit.doctype.membership.membership import verify_signature
+
class Donation(Document):
def validate(self):
if not self.donor or not frappe.db.exists('Donor', self.donor):
@@ -167,7 +171,7 @@
def get_company_for_donations():
company = frappe.db.get_single_value('Non Profit Settings', 'donation_company')
if not company:
- from erpnext.healthcare.setup import get_company
+ from erpnext.non_profit.utils import get_company
company = get_company()
return company
@@ -217,4 +221,3 @@
sendmail_to_system_managers(_('[Important] [ERPNext] Razorpay donation webhook failed, please check.'), content)
except Exception:
pass
-
diff --git a/erpnext/non_profit/doctype/donation/donation_dashboard.py b/erpnext/non_profit/doctype/donation/donation_dashboard.py
index 7e25c8d..4a16077 100644
--- a/erpnext/non_profit/doctype/donation/donation_dashboard.py
+++ b/erpnext/non_profit/doctype/donation/donation_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'donation',
@@ -13,4 +15,4 @@
'items': ['Payment Entry']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py
index bbe9bf5..6b9ade9 100644
--- a/erpnext/non_profit/doctype/donation/test_donation.py
+++ b/erpnext/non_profit/doctype/donation/test_donation.py
@@ -3,10 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
from erpnext.non_profit.doctype.donation.donation import create_donation
+
class TestDonation(unittest.TestCase):
def setUp(self):
create_donor_type()
@@ -73,4 +76,4 @@
'company': '_Test Company',
'default_account': 'Cash - _TC'
}]
- }).insert()
\ No newline at end of file
+ }).insert()
diff --git a/erpnext/non_profit/doctype/donor/donor.py b/erpnext/non_profit/doctype/donor/donor.py
index fb70e59..a46163a 100644
--- a/erpnext/non_profit/doctype/donor/donor.py
+++ b/erpnext/non_profit/doctype/donor/donor.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
+
from frappe.contacts.address_and_contact import load_address_and_contact
+from frappe.model.document import Document
+
class Donor(Document):
def onload(self):
@@ -15,4 +17,3 @@
from frappe.utils import validate_email_address
if self.email:
validate_email_address(self.email.strip(), True)
-
diff --git a/erpnext/non_profit/doctype/donor/test_donor.py b/erpnext/non_profit/doctype/donor/test_donor.py
index 3b6724e..5ce0199 100644
--- a/erpnext/non_profit/doctype/donor/test_donor.py
+++ b/erpnext/non_profit/doctype/donor/test_donor.py
@@ -5,5 +5,6 @@
import unittest
+
class TestDonor(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/donor_type/donor_type.py b/erpnext/non_profit/doctype/donor_type/donor_type.py
index e9262ac..4d34725 100644
--- a/erpnext/non_profit/doctype/donor_type/donor_type.py
+++ b/erpnext/non_profit/doctype/donor_type/donor_type.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class DonorType(Document):
pass
diff --git a/erpnext/non_profit/doctype/donor_type/test_donor_type.js b/erpnext/non_profit/doctype/donor_type/test_donor_type.js
deleted file mode 100644
index 22dc18e..0000000
--- a/erpnext/non_profit/doctype/donor_type/test_donor_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Donor Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Member
- () => frappe.tests.make('Donor Type', [
- // values to be set
- {donor_type: 'Test Organization'},
- ]),
- () => {
- assert.equal(cur_frm.doc.donor_type, 'Test Organization');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/donor_type/test_donor_type.py b/erpnext/non_profit/doctype/donor_type/test_donor_type.py
index e793913..7857ec5 100644
--- a/erpnext/non_profit/doctype/donor_type/test_donor_type.py
+++ b/erpnext/non_profit/doctype/donor_type/test_donor_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-
import unittest
+
class TestDonorType(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/grant_application/grant_application.py b/erpnext/non_profit/doctype/grant_application/grant_application.py
index f0123b2..92a6256 100644
--- a/erpnext/non_profit/doctype/grant_application/grant_application.py
+++ b/erpnext/non_profit/doctype/grant_application/grant_application.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.website.website_generator import WebsiteGenerator
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.utils import get_url
+from frappe.website.website_generator import WebsiteGenerator
+
class GrantApplication(WebsiteGenerator):
_website = frappe._dict(
@@ -55,4 +57,4 @@
grant.save()
frappe.db.commit()
- frappe.msgprint(_("Review Invitation Sent"))
\ No newline at end of file
+ frappe.msgprint(_("Review Invitation Sent"))
diff --git a/erpnext/non_profit/doctype/grant_application/test_grant_application.py b/erpnext/non_profit/doctype/grant_application/test_grant_application.py
index da16acf..d158091 100644
--- a/erpnext/non_profit/doctype/grant_application/test_grant_application.py
+++ b/erpnext/non_profit/doctype/grant_application/test_grant_application.py
@@ -5,5 +5,6 @@
import unittest
+
class TestGrantApplication(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/member/member.js b/erpnext/non_profit/doctype/member/member.js
index 6b8f1b1..e58ec0f 100644
--- a/erpnext/non_profit/doctype/member/member.js
+++ b/erpnext/non_profit/doctype/member/member.js
@@ -61,4 +61,4 @@
}
});
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index 67828d6..f7e7f10 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -3,14 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.model.document import Document
from frappe.contacts.address_and_contact import load_address_and_contact
-from frappe.utils import cint, get_link_to_form
from frappe.integrations.utils import get_payment_gateway_controller
+from frappe.model.document import Document
+from frappe.utils import cint, get_link_to_form
+
from erpnext.non_profit.doctype.membership_type.membership_type import get_membership_type
+
class Member(Document):
def onload(self):
"""Load address and contacts in `__onload`"""
diff --git a/erpnext/non_profit/doctype/member/member_dashboard.py b/erpnext/non_profit/doctype/member/member_dashboard.py
index 743db25..ff929a5 100644
--- a/erpnext/non_profit/doctype/member/member_dashboard.py
+++ b/erpnext/non_profit/doctype/member/member_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/non_profit/doctype/member/test_member.py b/erpnext/non_profit/doctype/member/test_member.py
index 748a500..38ad87f 100644
--- a/erpnext/non_profit/doctype/member/test_member.py
+++ b/erpnext/non_profit/doctype/member/test_member.py
@@ -5,5 +5,6 @@
import unittest
+
class TestMember(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index b584116..8522d66 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -3,17 +3,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import json
+from datetime import datetime
+
import frappe
import six
-import os
-from datetime import datetime
-from frappe.model.document import Document
-from frappe.email import sendmail_to_system_managers
-from frappe.utils import add_days, add_years, nowdate, getdate, add_months, get_link_to_form
-from erpnext.non_profit.doctype.member.member import create_member
from frappe import _
+from frappe.email import sendmail_to_system_managers
+from frappe.model.document import Document
+from frappe.utils import add_days, add_months, add_years, get_link_to_form, getdate, nowdate
+
import erpnext
+from erpnext.non_profit.doctype.member.member import create_member
+
class Membership(Document):
def validate(self):
@@ -207,7 +210,7 @@
try:
return frappe.get_doc("Member", members[0]["name"])
- except:
+ except Exception:
return None
@@ -352,7 +355,7 @@
def get_company_for_memberships():
company = frappe.db.get_single_value("Non Profit Settings", "company")
if not company:
- from erpnext.healthcare.setup import get_company
+ from erpnext.non_profit.utils import get_company
company = get_company()
return company
@@ -393,7 +396,7 @@
""".format(get_link_to_form("Error Log", log.name))
sendmail_to_system_managers("[Important] [ERPNext] Razorpay membership webhook failed , please check.", content)
- except:
+ except Exception:
pass
@@ -402,7 +405,7 @@
try:
return plan[0]["name"]
- except:
+ except Exception:
return None
diff --git a/erpnext/non_profit/doctype/membership/test_membership.js b/erpnext/non_profit/doctype/membership/test_membership.js
deleted file mode 100644
index 24c85c6..0000000
--- a/erpnext/non_profit/doctype/membership/test_membership.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Membership", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Membership
- () => frappe.tests.make('Membership', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py
index 0f5a9be..5f52cda 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -2,12 +2,16 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
+from frappe.utils import add_months, nowdate
+
import erpnext
from erpnext.non_profit.doctype.member.member import create_member
from erpnext.non_profit.doctype.membership.membership import update_halted_razorpay_subscription
-from frappe.utils import nowdate, add_months
+
class TestMembership(unittest.TestCase):
def setUp(self):
@@ -159,4 +163,4 @@
}
}
}
- }
\ No newline at end of file
+ }
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.py b/erpnext/non_profit/doctype/membership_type/membership_type.py
index 022829b..1b847d9 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.py
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
+
import frappe
from frappe import _
+from frappe.model.document import Document
+
class MembershipType(Document):
def validate(self):
@@ -15,4 +17,4 @@
frappe.throw(_("The Linked Item should be a service item"))
def get_membership_type(razorpay_id):
- return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id})
\ No newline at end of file
+ return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id})
diff --git a/erpnext/non_profit/doctype/membership_type/test_membership_type.py b/erpnext/non_profit/doctype/membership_type/test_membership_type.py
index d2c9bee..2503ba1 100644
--- a/erpnext/non_profit/doctype/membership_type/test_membership_type.py
+++ b/erpnext/non_profit/doctype/membership_type/test_membership_type.py
@@ -5,5 +5,6 @@
import unittest
+
class TestMembershipType(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py
index a84cc2c..cb365cb 100644
--- a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py
+++ b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.integrations.utils import get_payment_gateway_controller
from frappe.model.document import Document
+
class NonProfitSettings(Document):
@frappe.whitelist()
def generate_webhook_secret(self, field="membership_webhook_secret"):
@@ -35,4 +37,4 @@
def get_plans_for_membership(*args, **kwargs):
controller = get_payment_gateway_controller("Razorpay")
plans = controller.get_plans()
- return [plan.get("item") for plan in plans.get("items")]
\ No newline at end of file
+ return [plan.get("item") for plan in plans.get("items")]
diff --git a/erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py b/erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py
index 3f0ede3..a0a5403 100644
--- a/erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py
+++ b/erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestNonProfitSettings(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/volunteer/test_volunteer.py b/erpnext/non_profit/doctype/volunteer/test_volunteer.py
index 6f3bee0..346eac5 100644
--- a/erpnext/non_profit/doctype/volunteer/test_volunteer.py
+++ b/erpnext/non_profit/doctype/volunteer/test_volunteer.py
@@ -5,5 +5,6 @@
import unittest
+
class TestVolunteer(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/volunteer/volunteer.py b/erpnext/non_profit/doctype/volunteer/volunteer.py
index 699868a..6c9232b 100644
--- a/erpnext/non_profit/doctype/volunteer/volunteer.py
+++ b/erpnext/non_profit/doctype/volunteer/volunteer.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
+
from frappe.contacts.address_and_contact import load_address_and_contact
+from frappe.model.document import Document
+
class Volunteer(Document):
def onload(self):
diff --git a/erpnext/non_profit/doctype/volunteer_skill/volunteer_skill.py b/erpnext/non_profit/doctype/volunteer_skill/volunteer_skill.py
index dc9f823..3422ec2 100644
--- a/erpnext/non_profit/doctype/volunteer_skill/volunteer_skill.py
+++ b/erpnext/non_profit/doctype/volunteer_skill/volunteer_skill.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class VolunteerSkill(Document):
pass
diff --git a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.py b/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.py
index 78f65c7..2c64d21 100644
--- a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.py
+++ b/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.py
@@ -5,5 +5,6 @@
import unittest
+
class TestVolunteerType(unittest.TestCase):
pass
diff --git a/erpnext/non_profit/doctype/volunteer_type/volunteer_type.py b/erpnext/non_profit/doctype/volunteer_type/volunteer_type.py
index 9776402..116f5d8 100644
--- a/erpnext/non_profit/doctype/volunteer_type/volunteer_type.py
+++ b/erpnext/non_profit/doctype/volunteer_type/volunteer_type.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class VolunteerType(Document):
pass
diff --git a/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py b/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py
index 122db45..2167b65 100644
--- a/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py
+++ b/erpnext/non_profit/report/expiring_memberships/expiring_memberships.py
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _,msgprint
+from frappe import _
+
def execute(filters=None):
columns = get_columns(filters)
diff --git a/erpnext/non_profit/utils.py b/erpnext/non_profit/utils.py
new file mode 100644
index 0000000..47ea5f5
--- /dev/null
+++ b/erpnext/non_profit/utils.py
@@ -0,0 +1,12 @@
+import frappe
+
+
+def get_company():
+ company = frappe.defaults.get_defaults().company
+ if company:
+ return company
+ else:
+ company = frappe.get_list("Company", limit=1)
+ if company:
+ return company[0].name
+ return None
diff --git a/erpnext/non_profit/web_form/certification_application/certification_application.py b/erpnext/non_profit/web_form/certification_application/certification_application.py
index 2334f8b..f57de91 100644
--- a/erpnext/non_profit/web_form/certification_application/certification_application.py
+++ b/erpnext/non_profit/web_form/certification_application/certification_application.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/non_profit/web_form/certification_application_usd/certification_application_usd.py b/erpnext/non_profit/web_form/certification_application_usd/certification_application_usd.py
index 2334f8b..f57de91 100644
--- a/erpnext/non_profit/web_form/certification_application_usd/certification_application_usd.py
+++ b/erpnext/non_profit/web_form/certification_application_usd/certification_application_usd.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/non_profit/web_form/grant_application/grant_application.js b/erpnext/non_profit/web_form/grant_application/grant_application.js
index 7da3f1f..f09e540 100644
--- a/erpnext/non_profit/web_form/grant_application/grant_application.js
+++ b/erpnext/non_profit/web_form/grant_application/grant_application.js
@@ -1,3 +1,3 @@
frappe.ready(function() {
// bind events here
-});
\ No newline at end of file
+});
diff --git a/erpnext/non_profit/web_form/grant_application/grant_application.py b/erpnext/non_profit/web_form/grant_application/grant_application.py
index 7666ef6..dab0e9f 100644
--- a/erpnext/non_profit/web_form/grant_application/grant_application.py
+++ b/erpnext/non_profit/web_form/grant_application/grant_application.py
@@ -1,8 +1,7 @@
from __future__ import unicode_literals
+
def get_context(context):
context.no_cache = True
context.parents = [dict(label='View All ',
route='grant-application', title='View All')]
-
-
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0fc50c5..c55fb0a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -30,15 +30,12 @@
erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
erpnext.patches.v11_0.uom_conversion_data #30-06-2018
erpnext.patches.v11_0.update_account_type_in_party_type
-erpnext.patches.v11_0.rename_healthcare_doctype_and_fields
erpnext.patches.v11_0.rename_supplier_type_to_supplier_group
erpnext.patches.v10_1.transfer_subscription_to_auto_repeat
erpnext.patches.v11_0.update_brand_in_item_price
erpnext.patches.v11_0.create_default_success_action
-erpnext.patches.v11_0.add_healthcare_service_unit_tree_root
erpnext.patches.v11_0.rename_field_max_days_allowed
erpnext.patches.v11_0.create_salary_structure_assignments
-erpnext.patches.v11_0.rename_health_insurance
erpnext.patches.v11_0.rebuild_tree_for_company
erpnext.patches.v11_0.create_department_records_for_each_company
erpnext.patches.v11_0.make_location_from_warehouse
@@ -65,9 +62,7 @@
erpnext.patches.v11_0.reset_publish_in_hub_for_all_items
erpnext.patches.v11_0.update_hub_url # 2018-08-31 # 2018-09-03
erpnext.patches.v11_0.make_job_card
-erpnext.patches.v11_0.redesign_healthcare_billing_work_flow
erpnext.patches.v10_0.delete_hub_documents # 12-08-2018
-erpnext.patches.v11_0.rename_healthcare_fields
erpnext.patches.v11_0.add_default_dispatch_notification_template
erpnext.patches.v11_0.add_market_segments
erpnext.patches.v11_0.add_sales_stages
@@ -169,7 +164,6 @@
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
erpnext.patches.v12_0.rename_account_type_doctype
erpnext.patches.v12_0.recalculate_requested_qty_in_bin
-erpnext.patches.v12_0.update_healthcare_refactored_changes
erpnext.patches.v12_0.set_total_batch_quantity
erpnext.patches.v12_0.rename_mws_settings_fields
erpnext.patches.v12_0.set_updated_purpose_in_pick_list
@@ -178,7 +172,6 @@
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
erpnext.patches.v12_0.fix_quotation_expired_status
-erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
erpnext.patches.v12_0.rename_pos_closing_doctype
erpnext.patches.v13_0.replace_pos_payment_mode_table #2020-12-29
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
@@ -196,7 +189,6 @@
execute:frappe.reload_doc('desk', 'doctype', 'number_card_link')
execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
-erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2021-04-16
erpnext.patches.v12_0.update_bom_in_so_mr
execute:frappe.delete_doc("Report", "Department Analytics")
execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True)
@@ -214,7 +206,7 @@
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")
erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020
erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020
-execute:frappe.reload_doc("regional", "doctype", "e_invoice_settings")
+erpnext.patches.v12_0.create_itc_reversal_custom_fields
erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020
erpnext.patches.v13_0.loyalty_points_entry_for_pos_invoice #22-07-2020
erpnext.patches.v12_0.add_taxjar_integration_field
@@ -222,7 +214,6 @@
erpnext.patches.v13_0.delete_report_requested_items_to_order
erpnext.patches.v12_0.update_item_tax_template_company
erpnext.patches.v13_0.move_branch_code_to_bank_account
-erpnext.patches.v13_0.healthcare_lab_module_rename_doctypes
erpnext.patches.v13_0.add_standard_navbar_items #2021-03-24
erpnext.patches.v13_0.stock_entry_enhancements
erpnext.patches.v12_0.update_state_code_for_daman_and_diu
@@ -236,8 +227,6 @@
erpnext.patches.v13_0.set_app_name
erpnext.patches.v13_0.print_uom_after_quantity_patch
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
-erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
-erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02
erpnext.patches.v13_0.updates_for_multi_currency_payroll
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
@@ -252,35 +241,29 @@
erpnext.patches.v13_0.update_project_template_tasks
erpnext.patches.v13_0.set_company_in_leave_ledger_entry
erpnext.patches.v13_0.convert_qi_parameter_to_link_field
-erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
erpnext.patches.v13_0.update_payment_terms_outstanding
erpnext.patches.v12_0.add_state_code_for_ladakh
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
-erpnext.patches.v12_0.update_vehicle_no_reqd_condition
-erpnext.patches.v12_0.add_einvoice_status_field #2021-03-17
-erpnext.patches.v12_0.add_einvoice_summary_report_permissions
+erpnext.patches.v13_0.update_vehicle_no_reqd_condition
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
erpnext.patches.v13_0.setup_uae_vat_fields
execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
-erpnext.patches.v12_0.add_company_link_to_einvoice_settings
-erpnext.patches.v13_0.rename_discharge_date_in_ip_record
erpnext.patches.v12_0.create_taxable_value_field
erpnext.patches.v12_0.add_gst_category_in_delivery_note
erpnext.patches.v12_0.purchase_receipt_status
-erpnext.patches.v12_0.create_itc_reversal_custom_fields
erpnext.patches.v13_0.fix_non_unique_represents_company
erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
erpnext.patches.v13_0.update_shipment_status
erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
-erpnext.patches.v12_0.add_ewaybill_validity_field
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)
erpnext.patches.v13_0.update_timesheet_changes
erpnext.patches.v13_0.add_doctype_to_sla #14-06-2021
@@ -294,9 +277,29 @@
erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
erpnext.patches.v13_0.update_subscription_status_in_memberships
erpnext.patches.v13_0.update_amt_in_work_order_required_items
-erpnext.patches.v12_0.show_einvoice_irn_cancelled_field
erpnext.patches.v13_0.delete_orphaned_tables
erpnext.patches.v13_0.update_export_type_for_gst #2021-08-16
erpnext.patches.v13_0.update_tds_check_field #3
erpnext.patches.v13_0.add_custom_field_for_south_africa #2
+erpnext.patches.v13_0.update_recipient_email_digest
erpnext.patches.v13_0.shopify_deprecation_warning
+erpnext.patches.v13_0.migrate_stripe_api
+erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
+erpnext.patches.v13_0.einvoicing_deprecation_warning
+erpnext.patches.v13_0.custom_fields_for_taxjar_integration
+erpnext.patches.v14_0.delete_einvoicing_doctypes
+erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
+erpnext.patches.v13_0.validate_options_for_data_field
+erpnext.patches.v13_0.create_gst_payment_entry_fields
+erpnext.patches.v14_0.delete_shopify_doctypes
+erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
+erpnext.patches.v13_0.update_dates_in_tax_withholding_category
+erpnext.patches.v14_0.update_opportunity_currency_fields
+erpnext.patches.v13_0.gst_fields_for_pos_invoice
+erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
+erpnext.patches.v13_0.trim_sales_invoice_custom_field_length
+erpnext.patches.v13_0.create_custom_field_for_finance_book
+erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries
+erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
+erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
+erpnext.patches.v13_0.add_default_interview_notification_templates
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 d607b2f..5c28597 100644
--- a/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
+++ b/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
+
from erpnext.setup.install import create_default_cash_flow_mapper_templates
diff --git a/erpnext/patches/v10_0/delete_hub_documents.py b/erpnext/patches/v10_0/delete_hub_documents.py
index f6a1499..16c7abf 100644
--- a/erpnext/patches/v10_0/delete_hub_documents.py
+++ b/erpnext/patches/v10_0/delete_hub_documents.py
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.utils.rename_field import rename_field
+
def execute():
for dt, dn in (("Page", "Hub"), ("DocType", "Hub Settings"), ("DocType", "Hub Category")):
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 0315ae7..a3e4957 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
@@ -2,9 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
from erpnext.setup.doctype.company.company import install_country_fixtures
+
def execute():
frappe.reload_doc('regional', 'report', 'fichier_des_ecritures_comptables_[fec]')
for d in frappe.get_all('Company', filters = {'country': 'France'}):
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 102b6da..a3c61a5 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
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
@@ -48,4 +49,4 @@
return obj
def get_setting_companies():
- return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True)
\ No newline at end of file
+ return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True)
diff --git a/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py b/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py
index 2e30951..4fc419e 100644
--- a/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py
+++ b/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py
@@ -1,10 +1,12 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.table_exists("Offer Letter") and not frappe.db.table_exists("Job Offer"):
frappe.rename_doc("DocType", "Offer Letter", "Job Offer", force=True)
frappe.rename_doc("DocType", "Offer Letter Term", "Job Offer Term", force=True)
frappe.reload_doc("hr", "doctype", "job_offer")
frappe.reload_doc("hr", "doctype", "job_offer_term")
- frappe.delete_doc("Print Format", "Offer Letter")
\ No newline at end of file
+ frappe.delete_doc("Print Format", "Offer Letter")
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 48fa222..1b8c6fb 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc("accounts", "doctype", "pricing_rule")
@@ -11,4 +13,4 @@
except Exception as e:
if e.args[0]!=1054:
- raise
\ No newline at end of file
+ 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 c413931..2a3f1c0 100644
--- a/erpnext/patches/v10_0/set_currency_in_pricing_rule.py
+++ b/erpnext/patches/v10_0/set_currency_in_pricing_rule.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doctype("Pricing Rule")
diff --git a/erpnext/patches/v10_0/update_translatable_fields.py b/erpnext/patches/v10_0/update_translatable_fields.py
index 9d6bda7..2c55a052 100644
--- a/erpnext/patches/v10_0/update_translatable_fields.py
+++ b/erpnext/patches/v10_0/update_translatable_fields.py
@@ -4,6 +4,7 @@
import frappe
+
def execute():
'''
Enable translatable in these fields
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 3d1a88e..2d5b0c5 100644
--- a/erpnext/patches/v10_1/transfer_subscription_to_auto_repeat.py
+++ b/erpnext/patches/v10_1/transfer_subscription_to_auto_repeat.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
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 f4c1895..197b3b7 100644
--- a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
+++ b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import os
import frappe
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 f722be2..f8538df 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
@@ -1,7 +1,11 @@
from __future__ import unicode_literals
-import frappe, os
+
+import os
+
+import frappe
from frappe import _
+
def execute():
frappe.reload_doc("email", "doctype", "email_template")
@@ -27,4 +31,3 @@
'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 eecf755..74b93ef 100644
--- a/erpnext/patches/v11_0/add_expense_claim_default_account.py
+++ b/erpnext/patches/v11_0/add_expense_claim_default_account.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("setup", "doctype", "company")
@@ -8,4 +10,4 @@
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)
\ No newline at end of file
+ frappe.db.set_value("Company", company.name, "default_expense_claim_payable_account", company.default_payable_account)
diff --git a/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py b/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py
deleted file mode 100644
index d956052..0000000
--- a/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def execute():
- """ assign lft and rgt appropriately """
- if "Healthcare" not in frappe.get_active_domains():
- return
-
- frappe.reload_doc("healthcare", "doctype", "healthcare_service_unit")
- frappe.reload_doc("healthcare", "doctype", "healthcare_service_unit_type")
- company = frappe.get_value("Company", {"domain": "Healthcare"}, "name")
-
- if company:
- frappe.get_doc({
- 'doctype': 'Healthcare Service Unit',
- 'healthcare_service_unit_name': _('All Healthcare Service Units'),
- 'is_group': 1,
- 'company': company
- }).insert(ignore_permissions=True)
-
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 5a30c78..08ad855 100644
--- a/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
+++ b/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("assets", "doctype", "Location")
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")
\ No newline at end of file
+ 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 2a15ad1..6849b27 100644
--- a/erpnext/patches/v11_0/add_item_group_defaults.py
+++ b/erpnext/patches/v11_0/add_item_group_defaults.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
'''
diff --git a/erpnext/patches/v11_0/add_market_segments.py b/erpnext/patches/v11_0/add_market_segments.py
index ed47d42..e7cc7d1 100644
--- a/erpnext/patches/v11_0/add_market_segments.py
+++ b/erpnext/patches/v11_0/add_market_segments.py
@@ -1,12 +1,13 @@
from __future__ import unicode_literals
import frappe
-from frappe import _
+
from erpnext.setup.setup_wizard.operations.install_fixtures import add_market_segments
+
def execute():
frappe.reload_doc('crm', 'doctype', 'market_segment')
frappe.local.lang = frappe.db.get_default("lang") or 'en'
- add_market_segments()
\ No newline at end of file
+ add_market_segments()
diff --git a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py
index 83b2a4c..9df1b58 100644
--- a/erpnext/patches/v11_0/add_permissions_in_gst_settings.py
+++ b/erpnext/patches/v11_0/add_permissions_in_gst_settings.py
@@ -1,6 +1,8 @@
import frappe
+
from erpnext.regional.india.setup import add_permissions
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
diff --git a/erpnext/patches/v11_0/add_sales_stages.py b/erpnext/patches/v11_0/add_sales_stages.py
index ac2ae15..23e4874 100644
--- a/erpnext/patches/v11_0/add_sales_stages.py
+++ b/erpnext/patches/v11_0/add_sales_stages.py
@@ -1,11 +1,13 @@
from __future__ import unicode_literals
+
import frappe
-from frappe import _
+
from erpnext.setup.setup_wizard.operations.install_fixtures import add_sale_stages
+
def execute():
frappe.reload_doc('crm', 'doctype', 'sales_stage')
frappe.local.lang = frappe.db.get_default("lang") or 'en'
- add_sale_stages()
\ No newline at end of file
+ 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 462f830..5eaf212 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('setup', 'doctype', 'currency_exchange')
- frappe.db.sql("""update `tabCurrency Exchange` set for_buying = 1, for_selling = 1""")
\ No newline at end of file
+ frappe.db.sql("""update `tabCurrency Exchange` set for_buying = 1, for_selling = 1""")
diff --git a/erpnext/patches/v11_0/create_default_success_action.py b/erpnext/patches/v11_0/create_default_success_action.py
index 31feff2..4a59837 100644
--- a/erpnext/patches/v11_0/create_default_success_action.py
+++ b/erpnext/patches/v11_0/create_default_success_action.py
@@ -1,7 +1,10 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.setup.install import create_default_success_action
+
def execute():
frappe.reload_doc("core", "doctype", "success_action")
create_default_success_action()
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 e9b5950..7799a65 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
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils.nestedset import rebuild_tree
+
def execute():
frappe.local.lang = frappe.db.get_default("lang") or 'en'
diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py
index a908c16..c3cc9b6 100644
--- a/erpnext/patches/v11_0/create_salary_structure_assignments.py
+++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py
@@ -2,10 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from datetime import datetime
+
+import frappe
from frappe.utils import getdate
-from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment
+
+from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
+ DuplicateAssignment,
+)
+
def execute():
frappe.reload_doc('Payroll', 'doctype', 'Salary Structure')
@@ -69,4 +75,4 @@
except DuplicateAssignment:
pass
- frappe.db.sql("update `tabSalary Structure` set docstatus=1")
\ No newline at end of file
+ frappe.db.sql("update `tabSalary Structure` set docstatus=1")
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 591c521..e45d01c 100644
--- a/erpnext/patches/v11_0/drop_column_max_days_allowed.py
+++ b/erpnext/patches/v11_0/drop_column_max_days_allowed.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists("DocType", "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")
\ No newline at end of file
+ frappe.db.sql("alter table `tabLeave Type` drop column max_days_allowed")
diff --git a/erpnext/patches/v11_0/ewaybill_fields_gst_india.py b/erpnext/patches/v11_0/ewaybill_fields_gst_india.py
index 9925b70..a59291c 100644
--- a/erpnext/patches/v11_0/ewaybill_fields_gst_india.py
+++ b/erpnext/patches/v11_0/ewaybill_fields_gst_india.py
@@ -1,10 +1,13 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.regional.india.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
- make_custom_fields()
\ No newline at end of file
+ make_custom_fields()
diff --git a/erpnext/patches/v11_0/hr_ux_cleanups.py b/erpnext/patches/v11_0/hr_ux_cleanups.py
index 80476c8..b09f4a7 100644
--- a/erpnext/patches/v11_0/hr_ux_cleanups.py
+++ b/erpnext/patches/v11_0/hr_ux_cleanups.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doctype('Employee')
frappe.db.sql('update tabEmployee set first_name = employee_name')
@@ -10,4 +12,3 @@
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/inter_state_field_for_gst.py b/erpnext/patches/v11_0/inter_state_field_for_gst.py
index 730eebc..fa83af0 100644
--- a/erpnext/patches/v11_0/inter_state_field_for_gst.py
+++ b/erpnext/patches/v11_0/inter_state_field_for_gst.py
@@ -1,6 +1,9 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.regional.india.setup import make_custom_fields
+
+from erpnext.regional.india.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
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 ee709ac..1d3f8c1 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
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils.nestedset import rebuild_tree
+
def execute():
frappe.reload_doc('assets', 'doctype', 'asset_finance_book')
@@ -42,4 +43,4 @@
'frequency_of_depreciation': asset_category_doc.frequency_of_depreciation
})
- row.db_update()
\ No newline at end of file
+ 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 29d25c9..994df72 100644
--- a/erpnext/patches/v11_0/make_italian_localization_fields.py
+++ b/erpnext/patches/v11_0/make_italian_localization_fields.py
@@ -2,10 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-from erpnext.regional.italy.setup import make_custom_fields, setup_report
-from erpnext.regional.italy import state_codes
+
import frappe
+from erpnext.regional.italy import state_codes
+from erpnext.regional.italy.setup import make_custom_fields, setup_report
+
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'Italy'})
if not company:
diff --git a/erpnext/patches/v11_0/make_job_card.py b/erpnext/patches/v11_0/make_job_card.py
index 9c41c0b..e361d5a 100644
--- a/erpnext/patches/v11_0/make_job_card.py
+++ b/erpnext/patches/v11_0/make_job_card.py
@@ -2,9 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
from erpnext.manufacturing.doctype.work_order.work_order import create_job_card
+
def execute():
frappe.reload_doc('manufacturing', 'doctype', 'work_order')
frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
diff --git a/erpnext/patches/v11_0/make_location_from_warehouse.py b/erpnext/patches/v11_0/make_location_from_warehouse.py
index a307e8c..e855b3e 100644
--- a/erpnext/patches/v11_0/make_location_from_warehouse.py
+++ b/erpnext/patches/v11_0/make_location_from_warehouse.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils.nestedset import rebuild_tree
+
def execute():
if not frappe.db.get_value('Asset', {'docstatus': ('<', 2) }, 'name'): return
frappe.reload_doc('assets', 'doctype', 'location')
@@ -28,4 +30,3 @@
def get_parent_warehouse_name(warehouse):
return frappe.db.get_value('Warehouse', warehouse, 'warehouse_name')
-
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/make_quality_inspection_template.py b/erpnext/patches/v11_0/make_quality_inspection_template.py
index 9720af4..1c3d34e 100644
--- a/erpnext/patches/v11_0/make_quality_inspection_template.py
+++ b/erpnext/patches/v11_0/make_quality_inspection_template.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'quality_inspection_template')
frappe.reload_doc('stock', 'doctype', 'item')
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 c7c7635..42fdf13 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
'''
@@ -30,7 +32,7 @@
buying_cost_center, selling_cost_center, expense_account, income_account, default_supplier
FROM `tabItem`;
''', companies[0].name)
- except:
+ except Exception:
pass
else:
item_details = frappe.db.sql(""" SELECT name, default_warehouse,
@@ -93,4 +95,4 @@
`expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center`
)
VALUES {}
- '''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data))
\ No newline at end of file
+ '''.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 edab34c..accfa5e 100644
--- a/erpnext/patches/v11_0/move_leave_approvers_from_employee.py
+++ b/erpnext/patches/v11_0/move_leave_approvers_from_employee.py
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
+
import frappe
-from frappe import _
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc("hr", "doctype", "department_approver")
frappe.reload_doc("hr", "doctype", "employee")
@@ -31,4 +32,4 @@
if not len(department.leave_approvers):
department.append("leave_approvers",{
"approver": record.leave_approver
- }).db_insert()
\ No newline at end of file
+ }).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 4cb74c7..6caca47 100644
--- a/erpnext/patches/v11_0/rebuild_tree_for_company.py
+++ b/erpnext/patches/v11_0/rebuild_tree_for_company.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.utils.nestedset import rebuild_tree
+
def execute():
frappe.reload_doc("setup", "doctype", "company")
rebuild_tree('Company', 'parent_company')
diff --git a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py b/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py
deleted file mode 100644
index 7c8a822..0000000
--- a/erpnext/patches/v11_0/redesign_healthcare_billing_work_flow.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from erpnext.domains.healthcare import data
-from frappe.modules import scrub, get_doctype_module
-
-sales_invoice_referenced_doc = {
- "Patient Appointment": "sales_invoice",
- "Patient Encounter": "invoice",
- "Lab Test": "invoice",
- "Lab Prescription": "invoice",
- "Sample Collection": "invoice"
-}
-
-def execute():
- frappe.reload_doc('accounts', 'doctype', 'loyalty_program')
- frappe.reload_doc('accounts', 'doctype', 'sales_invoice_item')
-
- if "Healthcare" not in frappe.get_active_domains():
- return
-
- healthcare_custom_field_in_sales_invoice()
- for si_ref_doc in sales_invoice_referenced_doc:
- if frappe.db.exists('DocType', si_ref_doc):
- frappe.reload_doc(get_doctype_module(si_ref_doc), 'doctype', scrub(si_ref_doc))
-
- if frappe.db.has_column(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc]) \
- and frappe.db.has_column(si_ref_doc, 'invoiced'):
- # Set Reference DocType and Reference Docname
- doc_list = frappe.db.sql("""
- select name from `tab{0}`
- where {1} is not null
- """.format(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc]))
- if doc_list:
- frappe.reload_doc(get_doctype_module("Sales Invoice"), 'doctype', 'sales_invoice')
- for doc_id in doc_list:
- invoice_id = frappe.db.get_value(si_ref_doc, doc_id[0], sales_invoice_referenced_doc[si_ref_doc])
- if frappe.db.exists("Sales Invoice", invoice_id):
- if si_ref_doc == "Lab Test":
- template = frappe.db.get_value("Lab Test", doc_id[0], "template")
- if template:
- item = frappe.db.get_value("Lab Test Template", template, "item")
- if item:
- frappe.db.sql("""update `tabSales Invoice Item` set reference_dt = '{0}',
- reference_dn = '{1}' where parent = '{2}' and item_code='{3}'""".format\
- (si_ref_doc, doc_id[0], invoice_id, item))
- else:
- invoice = frappe.get_doc("Sales Invoice", invoice_id)
- for item_line in invoice.items:
- if not item_line.reference_dn:
- item_line.db_set({"reference_dt":si_ref_doc, "reference_dn": doc_id[0]})
- break
- # Documents mark invoiced for submitted sales invoice
- frappe.db.sql("""update `tab{0}` doc, `tabSales Invoice` si
- set doc.invoiced = 1 where si.docstatus = 1 and doc.{1} = si.name
- """.format(si_ref_doc, sales_invoice_referenced_doc[si_ref_doc]))
-
-def healthcare_custom_field_in_sales_invoice():
- frappe.reload_doc('healthcare', 'doctype', 'patient')
- frappe.reload_doc('healthcare', 'doctype', 'healthcare_practitioner')
- if data['custom_fields']:
- create_custom_fields(data['custom_fields'])
-
- frappe.db.sql("""
- delete from `tabCustom Field`
- where fieldname = 'appointment' and options = 'Patient Appointment'
- """)
diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py
index b997ba2..dd5cb63 100644
--- a/erpnext/patches/v11_0/refactor_autoname_naming.py
+++ b/erpnext/patches/v11_0/refactor_autoname_naming.py
@@ -117,4 +117,4 @@
def get_series_to_preserve(doctype):
series_to_preserve = frappe.db.get_value('DocType', doctype, 'autoname')
- return series_to_preserve
\ No newline at end of file
+ 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 b85ab66..fd4dbdc 100644
--- a/erpnext/patches/v11_0/refactor_naming_series.py
+++ b/erpnext/patches/v11_0/refactor_naming_series.py
@@ -15,7 +15,6 @@
'Blanket Order': 'MFG-BLR-.YYYY.-',
'C-Form': 'ACC-CF-.YYYY.-',
'Campaign': 'SAL-CAM-.YYYY.-',
- 'Clinical Procedure': 'HLC-CPR-.YYYY.-',
'Course Schedule': 'EDU-CSH-.YYYY.-',
'Customer': 'CUST-.YYYY.-',
'Delivery Note': 'MAT-DN-.YYYY.-',
@@ -27,12 +26,10 @@
'Fee Schedule': 'EDU-FSH-.YYYY.-',
'Fee Structure': 'EDU-FST-.YYYY.-',
'Fees': 'EDU-FEE-.YYYY.-',
- 'Inpatient Record': 'HLC-INP-.YYYY.-',
'Installation Note': 'MAT-INS-.YYYY.-',
'Instructor': 'EDU-INS-.YYYY.-',
'Issue': 'ISS-.YYYY.-',
'Journal Entry': 'ACC-JV-.YYYY.-',
- 'Lab Test': 'HLC-LT-.YYYY.-',
'Landed Cost Voucher': 'MAT-LCV-.YYYY.-',
'Lead': 'CRM-LEAD-.YYYY.-',
'Leave Allocation': 'HR-LAL-.YYYY.-',
@@ -43,9 +40,6 @@
'Member': 'NPO-MEM-.YYYY.-',
'Opportunity': 'CRM-OPP-.YYYY.-',
'Packing Slip': 'MAT-PAC-.YYYY.-',
- 'Patient': 'HLC-PAT-.YYYY.-',
- 'Patient Encounter': 'HLC-ENC-.YYYY.-',
- 'Patient Medical Record': 'HLC-PMR-.YYYY.-',
'Payment Entry': 'ACC-PAY-.YYYY.-',
'Payment Request': 'ACC-PRQ-.YYYY.-',
'Production Plan': 'MFG-PP-.YYYY.-',
@@ -132,4 +126,4 @@
def get_default_series(doctype):
field = frappe.get_meta(doctype).get_field("naming_series")
default_series = field.get('default', '') if field else ''
- return default_series
\ No newline at end of file
+ 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 97ddd41..caf74f5 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
@@ -1,5 +1,6 @@
import frappe
+
def execute():
'''Remove barcodes field from "Copy Fields to Variants" table because barcodes must be unique'''
diff --git a/erpnext/patches/v11_0/remove_modules_setup_page.py b/erpnext/patches/v11_0/remove_modules_setup_page.py
index bb0bdf5..eab3237 100644
--- a/erpnext/patches/v11_0/remove_modules_setup_page.py
+++ b/erpnext/patches/v11_0/remove_modules_setup_page.py
@@ -2,7 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.delete_doc("Page", "modules_setup")
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 8eb7016..5b2c286 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
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
# this patch should have been included with this PR https://github.com/frappe/erpnext/pull/14302
diff --git a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
index fad0cf7..707dff7 100644
--- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
+++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
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')
\ No newline at end of file
+ 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 882ec84..4ad6ea9 100644
--- a/erpnext/patches/v11_0/rename_bom_wo_fields.py
+++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
# updating column value to handle field change from Data to Currency
changed_field = "base_scrap_material_cost"
@@ -30,4 +32,4 @@
else:
frappe.db.sql(""" UPDATE `tab%s`
SET transfer_material_against = 'Work Order'
- WHERE docstatus < 2""" % (doctype))
\ No newline at end of file
+ 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 00ab562..61f3856 100644
--- a/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
+++ b/erpnext/patches/v11_0/rename_duplicate_item_code_values.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
items = []
items = frappe.db.sql("""select item_code from `tabItem` group by item_code having count(*) > 1""", as_dict=True)
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 4e99fac..48f73fb 100644
--- a/erpnext/patches/v11_0/rename_field_max_days_allowed.py
+++ b/erpnext/patches/v11_0/rename_field_max_days_allowed.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.db.sql("""
UPDATE `tabLeave Type`
diff --git a/erpnext/patches/v11_0/rename_health_insurance.py b/erpnext/patches/v11_0/rename_health_insurance.py
deleted file mode 100644
index e605071..0000000
--- a/erpnext/patches/v11_0/rename_health_insurance.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- frappe.rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True)
- frappe.reload_doc('hr', 'doctype', 'employee_health_insurance')
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py
deleted file mode 100644
index 9705681..0000000
--- a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
-
-field_rename_map = {
- "Patient Encounter": [
- ["consultation_time", "encounter_time"],
- ["consultation_date", "encounter_date"],
- ["consultation_comment", "encounter_comment"],
- ["physician", "practitioner"]
- ],
- "Fee Validity": [
- ["physician", "practitioner"]
- ],
- "Lab Test": [
- ["physician", "practitioner"]
- ],
- "Patient Appointment": [
- ["physician", "practitioner"],
- ["referring_physician", "referring_practitioner"]
- ],
- "Procedure Prescription": [
- ["physician", "practitioner"]
- ]
-}
-
-doc_rename_map = {
- "Physician Schedule Time Slot": "Healthcare Schedule Time Slot",
- "Physician Schedule": "Practitioner Schedule",
- "Physician Service Unit Schedule": "Practitioner Service Unit Schedule",
- "Consultation": "Patient Encounter",
- "Physician": "Healthcare Practitioner"
-}
-
-def execute():
- for dt in doc_rename_map:
- if frappe.db.exists('DocType', dt):
- frappe.rename_doc('DocType', dt, doc_rename_map[dt], force=True)
-
- for dn in field_rename_map:
- if frappe.db.exists('DocType', dn):
- 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 frappe.db.has_column(dt, field[0]):
- rename_field(dt, field[0], field[1])
-
- if frappe.db.exists('DocType', 'Practitioner Service Unit Schedule'):
- if frappe.db.has_column('Practitioner Service Unit Schedule', 'parentfield'):
- frappe.db.sql("""
- update `tabPractitioner Service Unit Schedule` set parentfield = 'practitioner_schedules'
- where parentfield = 'physician_schedules' and parenttype = 'Healthcare Practitioner'
- """)
-
- if frappe.db.exists("DocType", "Healthcare Practitioner"):
- frappe.reload_doc("healthcare", "doctype", "healthcare_practitioner")
- frappe.reload_doc("healthcare", "doctype", "practitioner_service_unit_schedule")
- if frappe.db.has_column('Healthcare Practitioner', 'physician_schedule'):
- for doc in frappe.get_all('Healthcare Practitioner'):
- _doc = frappe.get_doc('Healthcare Practitioner', doc.name)
- if _doc.physician_schedule:
- _doc.append('practitioner_schedules', {'schedule': _doc.physician_schedule})
- _doc.save()
diff --git a/erpnext/patches/v11_0/rename_healthcare_fields.py b/erpnext/patches/v11_0/rename_healthcare_fields.py
deleted file mode 100644
index 9aeb433..0000000
--- a/erpnext/patches/v11_0/rename_healthcare_fields.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
-
-lab_test_name = ["test_name", "lab_test_name"]
-lab_test_code = ["test_code", "lab_test_code"]
-lab_test_comment = ["test_comment", "lab_test_comment"]
-lab_test_created = ["test_created", "lab_test_created"]
-lab_test_template = ["test_template", "lab_test_template"]
-lab_test_rate = ["test_rate", "lab_test_rate"]
-lab_test_description = ["test_description", "lab_test_description"]
-lab_test_group = ["test_group", "lab_test_group"]
-lab_test_template_type = ["test_template_type", "lab_test_template_type"]
-lab_test_uom = ["test_uom", "lab_test_uom"]
-lab_test_normal_range = ["test_normal_range", "lab_test_normal_range"]
-lab_test_event = ["test_event", "lab_test_event"]
-lab_test_particulars = ["test_particulars", "lab_test_particulars"]
-
-field_rename_map = {
- "Lab Test Template": [lab_test_name, lab_test_code, lab_test_rate, lab_test_description,
- lab_test_group, lab_test_template_type, lab_test_uom, lab_test_normal_range],
- "Normal Test Items": [lab_test_name, lab_test_comment, lab_test_uom, lab_test_event],
- "Lab Test": [lab_test_name, lab_test_comment, lab_test_group],
- "Lab Prescription": [lab_test_name, lab_test_code, lab_test_comment, lab_test_created],
- "Lab Test Groups": [lab_test_template, lab_test_rate, lab_test_description],
- "Lab Test UOM": [lab_test_uom],
- "Normal Test Template": [lab_test_uom, lab_test_event],
- "Special Test Items": [lab_test_particulars]
-}
-
-
-def execute():
- for dt, field_list in field_rename_map.items():
- if frappe.db.exists('DocType', dt):
- frappe.reload_doc(get_doctype_module(dt), "doctype", scrub(dt))
- for field in field_list:
- if frappe.db.has_column(dt, field[0]):
- rename_field(dt, field[0], field[1])
-
- if frappe.db.exists('DocType', 'Lab Prescription'):
- if frappe.db.has_column('Lab Prescription', 'parentfield'):
- frappe.db.sql("""
- update `tabLab Prescription` set parentfield = 'lab_test_prescription'
- where parentfield = 'test_prescription'
- """)
-
- if frappe.db.exists('DocType', 'Lab Test Groups'):
- if frappe.db.has_column('Lab Test Groups', 'parentfield'):
- frappe.db.sql("""
- update `tabLab Test Groups` set parentfield = 'lab_test_groups'
- where parentfield = 'test_groups'
- """)
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 84f5518..a3d7970 100644
--- a/erpnext/patches/v11_0/rename_members_with_naming_series.py
+++ b/erpnext/patches/v11_0/rename_members_with_naming_series.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("non_profit", "doctype", "member")
old_named_members = frappe.get_all("Member", filters = {"name": ("not like", "MEM-%")})
diff --git a/erpnext/patches/v11_0/rename_overproduction_percent_field.py b/erpnext/patches/v11_0/rename_overproduction_percent_field.py
index 077829f..c7124af 100644
--- a/erpnext/patches/v11_0/rename_overproduction_percent_field.py
+++ b/erpnext/patches/v11_0/rename_overproduction_percent_field.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-from frappe.model.utils.rename_field import rename_field
+
import frappe
+from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
- rename_field('Manufacturing Settings', 'over_production_allowance_percentage', 'overproduction_percentage_for_sales_order')
\ No newline at end of file
+ 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 2f620f4..995f1af 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
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.rename_doc('DocType', 'Production Order', 'Work Order', force=True)
frappe.reload_doc('manufacturing', 'doctype', 'work_order')
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 c4b3838..2e53fb8 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
@@ -1,9 +1,11 @@
from __future__ import unicode_literals
+
import frappe
-from frappe.model.utils.rename_field import rename_field
from frappe import _
+from frappe.model.utils.rename_field import rename_field
from frappe.utils.nestedset import rebuild_tree
+
def execute():
if frappe.db.table_exists("Supplier Group"):
frappe.reload_doc('setup', 'doctype', '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 4f68440..894aaee 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
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
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')
\ No newline at end of file
+ rename_field('Project', 'to', 'to_time')
diff --git a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
index 56e95e0..a664baf 100644
--- a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
+++ b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
frappe.db.sql("""update `tabItem` set publish_in_hub = 0""")
diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
index 4622376..ff75424 100644
--- a/erpnext/patches/v11_0/set_default_email_template_in_hr.py
+++ b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
-from frappe import _
+
import frappe
+from frappe import _
+
def execute():
hr_settings = frappe.get_single("HR Settings")
diff --git a/erpnext/patches/v11_0/set_department_for_doctypes.py b/erpnext/patches/v11_0/set_department_for_doctypes.py
index 175d2a1..c969965 100644
--- a/erpnext/patches/v11_0/set_department_for_doctypes.py
+++ b/erpnext/patches/v11_0/set_department_for_doctypes.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
# Set department value based on employee value
diff --git a/erpnext/patches/v11_0/set_missing_gst_hsn_code.py b/erpnext/patches/v11_0/set_missing_gst_hsn_code.py
index 4353ef8..7a0a989 100644
--- a/erpnext/patches/v11_0/set_missing_gst_hsn_code.py
+++ b/erpnext/patches/v11_0/set_missing_gst_hsn_code.py
@@ -1,7 +1,10 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_html
+
def execute():
company = frappe.db.sql_list("select name from tabCompany where country = 'India'")
if not company:
@@ -41,4 +44,4 @@
for t in list(parent):
trans_doc = frappe.get_doc(dt, t)
hsnwise_tax = get_itemised_tax_breakup_html(trans_doc)
- frappe.db.set_value(dt, t, "other_charges_calculation", hsnwise_tax, update_modified=False)
\ No newline at end of file
+ frappe.db.set_value(dt, t, "other_charges_calculation", hsnwise_tax, update_modified=False)
diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py
index 2498888..b70dc35 100644
--- a/erpnext/patches/v11_0/set_salary_component_properties.py
+++ b/erpnext/patches/v11_0/set_salary_component_properties.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('Payroll', 'doctype', 'salary_detail')
frappe.reload_doc('Payroll', 'doctype', 'salary_component')
@@ -13,4 +15,4 @@
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')""")
\ No newline at end of file
+ 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 d0cabb3..da4d4bd 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.workflow import get_workflow_name
+
def execute():
for doctype in ['Expense Claim', 'Leave Application']:
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 7bd8577..7840d5e 100644
--- a/erpnext/patches/v11_0/set_user_permissions_for_department.py
+++ b/erpnext/patches/v11_0/set_user_permissions_for_department.py
@@ -1,12 +1,14 @@
from __future__ import unicode_literals
+
import frappe
+
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
+ 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)
@@ -16,4 +18,4 @@
pass
frappe.reload_doc("hr", "doctype", "department")
- frappe.db.sql("update tabDepartment set disabled=1 where ifnull(company, '') = ''")
\ No newline at end of file
+ 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 0f7fad7..66d1b6b 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
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
from frappe.desk.form.linked_with import get_linked_doctypes
@@ -58,4 +59,4 @@
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))
\ No newline at end of file
+ ), 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 91470b3..a408d86 100644
--- a/erpnext/patches/v11_0/uom_conversion_data.py
+++ b/erpnext/patches/v11_0/uom_conversion_data.py
@@ -1,5 +1,7 @@
from __future__ import unicode_literals
-import frappe, json
+
+import frappe
+
def execute():
from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data
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 efa04fd..e51874f 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
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('setup', 'doctype', 'party_type')
party_types = {'Customer': 'Receivable', 'Supplier': 'Payable',
'Employee': 'Payable', 'Member': 'Receivable', 'Shareholder': 'Payable', 'Student': 'Receivable'}
for party_type, account_type in party_types.items():
- frappe.db.set_value('Party Type', party_type, 'account_type', account_type)
\ No newline at end of file
+ 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 1b58c97..bfcfc9f 100644
--- a/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
+++ b/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
frappe.db.sql(""" update `tabItem` set include_item_in_manufacturing = 1
@@ -17,4 +19,4 @@
child.include_item_in_manufacturing = 1
where
child.item_code = item.name and ifnull(item.is_stock_item, 0) = 1
- """.format(doctype))
\ No newline at end of file
+ """.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 f2eeada..c3b18bd 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('buying', 'doctype', 'buying_settings')
frappe.db.set_value('Buying Settings', None, 'backflush_raw_materials_of_subcontract_based_on', 'BOM')
@@ -16,4 +18,4 @@
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'""")
\ No newline at end of file
+ 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 a8d3fab..a489378 100644
--- a/erpnext/patches/v11_0/update_brand_in_item_price.py
+++ b/erpnext/patches/v11_0/update_brand_in_item_price.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'item_price')
@@ -12,4 +14,4 @@
`tabItem Price`.brand = `tabItem`.brand
where
`tabItem Price`.item_code = `tabItem`.name
- and `tabItem`.brand is not null and `tabItem`.brand != ''""")
\ No newline at end of file
+ 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 42f017e..da25958 100755
--- a/erpnext/patches/v11_0/update_delivery_trip_status.py
+++ b/erpnext/patches/v11_0/update_delivery_trip_status.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('setup', 'doctype', 'global_defaults', force=True)
frappe.reload_doc('stock', 'doctype', 'delivery_trip')
diff --git a/erpnext/patches/v11_0/update_department_lft_rgt.py b/erpnext/patches/v11_0/update_department_lft_rgt.py
index b2f407b..f7ecf6e 100644
--- a/erpnext/patches/v11_0/update_department_lft_rgt.py
+++ b/erpnext/patches/v11_0/update_department_lft_rgt.py
@@ -4,6 +4,7 @@
from frappe import _
from frappe.utils.nestedset import rebuild_tree
+
def execute():
""" assign lft and rgt appropriately """
frappe.reload_doc("hr", "doctype", "department")
@@ -17,4 +18,4 @@
frappe.db.sql("""update `tabDepartment` set parent_department = '{0}'
where is_group = 0""".format(_('All Departments')))
- rebuild_tree("Department", "parent_department")
\ No newline at end of file
+ rebuild_tree("Department", "parent_department")
diff --git a/erpnext/patches/v11_0/update_hub_url.py b/erpnext/patches/v11_0/update_hub_url.py
index 6c6ca3c..c89b9b5 100644
--- a/erpnext/patches/v11_0/update_hub_url.py
+++ b/erpnext/patches/v11_0/update_hub_url.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('hub_node', 'doctype', 'Marketplace Settings')
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'marketplace_url', 'https://hubmarket.org')
diff --git a/erpnext/patches/v11_0/update_sales_partner_type.py b/erpnext/patches/v11_0/update_sales_partner_type.py
index b393926..1369805 100644
--- a/erpnext/patches/v11_0/update_sales_partner_type.py
+++ b/erpnext/patches/v11_0/update_sales_partner_type.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute():
from erpnext.setup.setup_wizard.operations.install_fixtures import default_sales_partner_type
diff --git a/erpnext/patches/v11_0/update_total_qty_field.py b/erpnext/patches/v11_0/update_total_qty_field.py
index 9407256..e79a5f9 100644
--- a/erpnext/patches/v11_0/update_total_qty_field.py
+++ b/erpnext/patches/v11_0/update_total_qty_field.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('buying', 'doctype', 'purchase_order')
frappe.reload_doc('buying', 'doctype', 'supplier_quotation')
diff --git a/erpnext/patches/v11_1/delete_bom_browser.py b/erpnext/patches/v11_1/delete_bom_browser.py
index 457f511..aad3df2 100644
--- a/erpnext/patches/v11_1/delete_bom_browser.py
+++ b/erpnext/patches/v11_1/delete_bom_browser.py
@@ -2,7 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
- frappe.delete_doc_if_exists('Page', 'bom-browser')
\ No newline at end of file
+ frappe.delete_doc_if_exists('Page', 'bom-browser')
diff --git a/erpnext/patches/v11_1/delete_scheduling_tool.py b/erpnext/patches/v11_1/delete_scheduling_tool.py
index b7ad28a..56d0dbf 100644
--- a/erpnext/patches/v11_1/delete_scheduling_tool.py
+++ b/erpnext/patches/v11_1/delete_scheduling_tool.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists("DocType", "Scheduling Tool"):
frappe.delete_doc("DocType", "Scheduling Tool", ignore_permissions=True)
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 6e708df..db0c345 100644
--- a/erpnext/patches/v11_1/make_job_card_time_logs.py
+++ b/erpnext/patches/v11_1/make_job_card_time_logs.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('manufacturing', 'doctype', 'job_card_time_log')
@@ -26,4 +28,4 @@
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 """)
\ No newline at end of file
+ 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 5b1251c..9ea6cd8 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doctype("Quotation")
frappe.db.sql(""" UPDATE `tabQuotation` set party_name = lead WHERE quotation_to = 'Lead' """)
@@ -11,4 +13,4 @@
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' """)
\ No newline at end of file
+ frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = customer WHERE opportunity_from = 'Customer' """)
diff --git a/erpnext/patches/v11_1/rename_depends_on_lwp.py b/erpnext/patches/v11_1/rename_depends_on_lwp.py
index a0f2536..95a8225 100644
--- a/erpnext/patches/v11_1/rename_depends_on_lwp.py
+++ b/erpnext/patches/v11_1/rename_depends_on_lwp.py
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import scrub
from frappe.model.utils.rename_field import rename_field
+
def execute():
for doctype in ("Salary Component", "Salary Detail"):
if "depends_on_lwp" in frappe.db.get_table_columns(doctype):
frappe.reload_doc("Payroll", "doctype", scrub(doctype))
- rename_field(doctype, "depends_on_lwp", "depends_on_payment_days")
\ No newline at end of file
+ rename_field(doctype, "depends_on_lwp", "depends_on_payment_days")
diff --git a/erpnext/patches/v11_1/renamed_delayed_item_report.py b/erpnext/patches/v11_1/renamed_delayed_item_report.py
index 222b9a0..2128563 100644
--- a/erpnext/patches/v11_1/renamed_delayed_item_report.py
+++ b/erpnext/patches/v11_1/renamed_delayed_item_report.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
for report in ["Delayed Order Item Summary", "Delayed Order Summary"]:
if frappe.db.exists("Report", report):
- frappe.delete_doc("Report", report)
\ No newline at end of file
+ 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 b13239f..4325490 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
stock_settings = frappe.get_doc('Stock Settings')
if stock_settings.default_warehouse and not frappe.db.exists("Warehouse", stock_settings.default_warehouse):
diff --git a/erpnext/patches/v11_1/set_missing_opportunity_from.py b/erpnext/patches/v11_1/set_missing_opportunity_from.py
index cb444b2..6569200 100644
--- a/erpnext/patches/v11_1/set_missing_opportunity_from.py
+++ b/erpnext/patches/v11_1/set_missing_opportunity_from.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doctype("Opportunity")
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 e2ef343..93d9f0e 100644
--- a/erpnext/patches/v11_1/set_missing_title_for_quotation.py
+++ b/erpnext/patches/v11_1/set_missing_title_for_quotation.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doctype("Quotation")
# update customer_name from Customer document if quotation_to is set to Customer
diff --git a/erpnext/patches/v11_1/set_salary_details_submittable.py b/erpnext/patches/v11_1/set_salary_details_submittable.py
index 6d847ec..4a4cf30 100644
--- a/erpnext/patches/v11_1/set_salary_details_submittable.py
+++ b/erpnext/patches/v11_1/set_salary_details_submittable.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.db.sql("""
update `tabSalary Structure` ss, `tabSalary Detail` sd
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 d41cff5..64db97e 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
@@ -1,9 +1,11 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.db.sql("""
update `tabMaterial Request`
set status='Manufactured'
where docstatus=1 and material_request_type='Manufacture' and per_ordered=100 and status != 'Stopped'
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v11_1/set_variant_based_on.py b/erpnext/patches/v11_1/set_variant_based_on.py
index 019eefd..b69767d 100644
--- a/erpnext/patches/v11_1/set_variant_based_on.py
+++ b/erpnext/patches/v11_1/set_variant_based_on.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.db.sql("""update tabItem set variant_based_on = 'Item Attribute'
where ifnull(variant_based_on, '') = ''
and (has_variants=1 or ifnull(variant_of, '') != '')
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v11_1/setup_guardian_role.py b/erpnext/patches/v11_1/setup_guardian_role.py
index 6ccfed9..bb33f19 100644
--- a/erpnext/patches/v11_1/setup_guardian_role.py
+++ b/erpnext/patches/v11_1/setup_guardian_role.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
if 'Education' in frappe.get_active_domains() and not frappe.db.exists("Role", "Guardian"):
doc = frappe.new_doc("Role")
diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py
index 544bc5e..33007af 100644
--- a/erpnext/patches/v11_1/update_bank_transaction_status.py
+++ b/erpnext/patches/v11_1/update_bank_transaction_status.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "bank_transaction")
@@ -23,4 +25,4 @@
WHERE
status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount)
and ifnull(allocated_amount, 0) > 0
- """)
\ No newline at end of file
+ """)
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 347dec1..22dabae 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
'''
default supplier was not set in the item defaults for multi company instance,
@@ -22,4 +24,4 @@
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 != '' """)
\ No newline at end of file
+ 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 5ccdec6..e7218b1 100644
--- a/erpnext/patches/v11_1/woocommerce_set_creation_user.py
+++ b/erpnext/patches/v11_1/woocommerce_set_creation_user.py
@@ -1,11 +1,13 @@
from __future__ import unicode_literals
+
import frappe
from frappe.utils import cint
+
def execute():
frappe.reload_doc("erpnext_integrations", "doctype","woocommerce_settings")
doc = frappe.get_doc("Woocommerce Settings")
if cint(doc.enable_sync):
doc.creation_user = doc.modified_by
- doc.save(ignore_permissions=True)
\ No newline at end of file
+ doc.save(ignore_permissions=True)
diff --git a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py
deleted file mode 100644
index b6bd5fa..0000000
--- a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company or not frappe.db.count('E Invoice User'):
- return
-
- frappe.reload_doc("regional", "doctype", "e_invoice_user")
- for creds in frappe.db.get_all('E Invoice User', fields=['name', 'gstin']):
- company_name = frappe.db.sql("""
- select dl.link_name from `tabAddress` a, `tabDynamic Link` dl
- where a.gstin = %s and dl.parent = a.name and dl.link_doctype = 'Company'
- """, (creds.get('gstin')))
- if company_name and len(company_name) > 0:
- frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0])
\ No newline at end of file
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 484f81a..384a1f5 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
@@ -6,6 +6,7 @@
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc("setup", "doctype", "company")
if frappe.db.has_column('Company', 'default_terms'):
@@ -14,6 +15,6 @@
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.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 4d649dd..7f39dfe 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'Italy'})
@@ -15,4 +17,4 @@
]
}
- create_custom_fields(custom_fields, update=True)
\ No newline at end of file
+ create_custom_fields(custom_fields, update=True)
diff --git a/erpnext/patches/v12_0/add_einvoice_status_field.py b/erpnext/patches/v12_0/add_einvoice_status_field.py
deleted file mode 100644
index 387e885..0000000
--- a/erpnext/patches/v12_0/add_einvoice_status_field.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from __future__ import unicode_literals
-import json
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-def execute():
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company:
- return
-
- # move hidden einvoice fields to a different section
- custom_fields = {
- 'Sales Invoice': [
- dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
- print_hide=1, hidden=1),
-
- dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
- no_copy=1, print_hide=1),
-
- dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
-
- dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date',
- no_copy=1, print_hide=1),
-
- dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
- no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
- no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
- no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
- options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
- hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
- ]
- }
- create_custom_fields(custom_fields, update=True)
-
- if frappe.db.exists('E Invoice Settings') and frappe.db.get_single_value('E Invoice Settings', 'enable'):
- frappe.db.sql('''
- UPDATE `tabSales Invoice` SET einvoice_status = 'Pending'
- WHERE
- posting_date >= '2021-04-01'
- AND ifnull(irn, '') = ''
- AND ifnull(`billing_address_gstin`, '') != ifnull(`company_gstin`, '')
- AND ifnull(gst_category, '') in ('Registered Regular', 'SEZ', 'Overseas', 'Deemed Export')
- ''')
-
- # set appropriate statuses
- frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Generated'
- WHERE ifnull(irn, '') != '' AND ifnull(irn_cancelled, 0) = 0''')
-
- frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Cancelled'
- WHERE ifnull(irn_cancelled, 0) = 1''')
-
- # set correct acknowledgement in e-invoices
- einvoices = frappe.get_all('Sales Invoice', {'irn': ['is', 'set']}, ['name', 'signed_einvoice'])
-
- if einvoices:
- for inv in einvoices:
- signed_einvoice = inv.get('signed_einvoice')
- if signed_einvoice:
- signed_einvoice = json.loads(signed_einvoice)
- frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_no', signed_einvoice.get('AckNo'), update_modified=False)
- frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py
deleted file mode 100644
index bf8f566..0000000
--- a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company:
- return
-
- if frappe.db.exists('Report', 'E-Invoice Summary') and \
- not frappe.db.get_value('Custom Role', dict(report='E-Invoice Summary')):
- frappe.get_doc(dict(
- doctype='Custom Role',
- report='E-Invoice Summary',
- roles= [
- dict(role='Accounts User'),
- dict(role='Accounts Manager')
- ]
- )).insert()
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py b/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py
index bb4b038..973da89 100644
--- a/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py
+++ b/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py
@@ -1,6 +1,7 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -16,4 +17,4 @@
'insert_after': 'customer_name_in_arabic',
'translatable': 0,
'owner': 'Administrator'
- })
\ No newline at end of file
+ })
diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
deleted file mode 100644
index 87d98f1..0000000
--- a/erpnext/patches/v12_0/add_ewaybill_validity_field.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-def execute():
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company:
- return
-
- custom_fields = {
- 'Sales Invoice': [
- dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
- depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill')
- ]
- }
- create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py
index 5bb6e3f..e05c821 100644
--- a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py
+++ b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py
@@ -1,7 +1,10 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.regional.india.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -38,5 +41,3 @@
WHERE fieldname = 'is_inter_state'
AND dt IN ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template')
""")
-
-
diff --git a/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py b/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
index 1208222..30e47cb 100644
--- a/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
+++ b/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
@@ -16,4 +18,4 @@
]
}
- create_custom_fields(custom_fields, update=True)
\ No newline at end of file
+ create_custom_fields(custom_fields, update=True)
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 485dd31..d765b93 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
@@ -11,4 +11,4 @@
SET
wo.item_name = item.item_name
""")
- frappe.db.commit()
\ No newline at end of file
+ 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 af9bf74..1d77e5a 100644
--- a/erpnext/patches/v12_0/add_permission_in_lower_deduction.py
+++ b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py
@@ -1,6 +1,7 @@
import frappe
from frappe.permissions import add_permission, update_permission_property
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
@@ -10,4 +11,4 @@
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)
\ No newline at end of file
+ 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 29a7b4b..6722b7b 100644
--- a/erpnext/patches/v12_0/add_state_code_for_ladakh.py
+++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
@@ -1,6 +1,8 @@
import frappe
+
from erpnext.regional.india import states
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
diff --git a/erpnext/patches/v12_0/add_taxjar_integration_field.py b/erpnext/patches/v12_0/add_taxjar_integration_field.py
index 4c823e1..d10a6d7 100644
--- a/erpnext/patches/v12_0/add_taxjar_integration_field.py
+++ b/erpnext/patches/v12_0/add_taxjar_integration_field.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import frappe
+
from erpnext.regional.united_states.setup import make_custom_fields
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 893f7a4..c3a422c 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
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'item_variant_attribute')
frappe.db.sql('''
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 657decf..02fbe62 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'accounting_dimension')
@@ -39,4 +41,4 @@
create_custom_field(doctype, df)
frappe.clear_cache(doctype=doctype)
- count += 1
\ No newline at end of file
+ count += 1
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 88233b4..35eaca7 100644
--- a/erpnext/patches/v12_0/create_default_energy_point_rules.py
+++ b/erpnext/patches/v12_0/create_default_energy_point_rules.py
@@ -1,6 +1,8 @@
import frappe
+
from erpnext.setup.install import create_default_energy_point_rules
+
def execute():
frappe.reload_doc('social', 'doctype', 'energy_point_rule')
- create_default_energy_point_rules()
\ No newline at end of file
+ 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 7feaffd..65265c4 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
@@ -1,7 +1,10 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.regional.united_states.setup import make_custom_fields
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True)
@@ -13,4 +16,4 @@
if not company:
return
- make_custom_fields()
\ No newline at end of file
+ make_custom_fields()
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 0078a53..9267ebf 100644
--- a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
+++ b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py
@@ -1,9 +1,12 @@
from __future__ import unicode_literals
+
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+
from erpnext.regional.india.utils import get_gst_accounts
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'}, fields=['name'])
if not company:
@@ -112,4 +115,4 @@
'itc_central_tax': values.get('itc_central_tax'),
'itc_state_tax': values['itc_state_tax'],
'itc_cess_amount': values['itc_cess_amount'],
- })
\ No newline at end of file
+ })
diff --git a/erpnext/patches/v12_0/create_taxable_value_field.py b/erpnext/patches/v12_0/create_taxable_value_field.py
index a0c9fcf..40de8d8 100644
--- a/erpnext/patches/v12_0/create_taxable_value_field.py
+++ b/erpnext/patches/v12_0/create_taxable_value_field.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
@@ -15,4 +17,4 @@
]
}
- create_custom_fields(custom_fields, update=True)
\ No newline at end of file
+ create_custom_fields(custom_fields, update=True)
diff --git a/erpnext/patches/v12_0/delete_priority_property_setter.py b/erpnext/patches/v12_0/delete_priority_property_setter.py
index 5927267..cacc463 100644
--- a/erpnext/patches/v12_0/delete_priority_property_setter.py
+++ b/erpnext/patches/v12_0/delete_priority_property_setter.py
@@ -1,9 +1,10 @@
import frappe
+
def execute():
frappe.db.sql("""
DELETE FROM `tabProperty Setter`
WHERE `tabProperty Setter`.doc_type='Issue'
AND `tabProperty Setter`.field_name='priority'
AND `tabProperty Setter`.property='options'
- """)
\ No newline at end of file
+ """)
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 3622df6..36f51bc 100644
--- a/erpnext/patches/v12_0/fix_percent_complete_for_projects.py
+++ b/erpnext/patches/v12_0/fix_percent_complete_for_projects.py
@@ -1,6 +1,7 @@
import frappe
from frappe.utils import flt
+
def execute():
for project in frappe.get_all("Project", fields=["name", "percent_complete_method"]):
total = frappe.db.count('Task', dict(project=project.name))
diff --git a/erpnext/patches/v12_0/fix_quotation_expired_status.py b/erpnext/patches/v12_0/fix_quotation_expired_status.py
index c8708d8..e5c4b8c 100644
--- a/erpnext/patches/v12_0/fix_quotation_expired_status.py
+++ b/erpnext/patches/v12_0/fix_quotation_expired_status.py
@@ -1,28 +1,29 @@
import frappe
+
def execute():
# fixes status of quotations which have status 'Expired' despite having valid sales order created
# filter out submitted expired quotations which has sales order created
cond = "qo.docstatus = 1 and qo.status = 'Expired'"
invalid_so_against_quo = """
- SELECT
+ SELECT
so.name FROM `tabSales Order` so, `tabSales Order Item` so_item
- WHERE
+ WHERE
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
-
+
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)
)
-
+
valid_so_against_quo = """
- SELECT
+ SELECT
so.name FROM `tabSales Order` so, `tabSales Order Item` so_item
- WHERE
+ WHERE
so_item.docstatus = 1 and so.docstatus = 1
and so_item.parent = so.name
and so_item.prevdoc_docname = qo.name
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index fe072d7..aed56d6 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils import getdate, today
+
def execute():
""" Generates leave ledger entries for leave allocation/application/encashment
for last allocation """
diff --git a/erpnext/patches/v12_0/make_item_manufacturer.py b/erpnext/patches/v12_0/make_item_manufacturer.py
index ebc2832..cfc2472 100644
--- a/erpnext/patches/v12_0/make_item_manufacturer.py
+++ b/erpnext/patches/v12_0/make_item_manufacturer.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("stock", "doctype", "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 a670ade..3e9d429 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'bank', force=1)
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 c9293b9..2662632 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
''' Move credit limit and bypass credit limit to the child table of customer credit limit '''
frappe.reload_doc("Selling", "doctype", "Customer Credit Limit")
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 6013eaa..55f5cd5 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
''' Move from due_advance_amount to pending_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 a6471eb..677a564 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
@@ -1,7 +1,9 @@
-import frappe
import json
-from six import iteritems
+
+import frappe
from frappe.model.naming import make_autoname
+from six import iteritems
+
def execute():
if "tax_type" not in frappe.db.get_table_columns("Item Tax"):
@@ -91,8 +93,9 @@
item_tax_template.title = make_autoname("Item Tax Template-.####")
for tax_type, tax_rate in iteritems(item_tax_map):
- account_details = frappe.db.get_value("Account", tax_type, ['name', 'account_type'], 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')
else:
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 d2bcb12..dafea28 100644
--- a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
+++ b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
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 548c1a4..72f4df5 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("setup", "doctype", "target_detail")
@@ -19,4 +21,4 @@
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")
\ No newline at end of file
+ frappe.delete_doc("Report", "Territory Target Variance Item Group-Wise")
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 8267df9..a19e9a9 100644
--- a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
+++ b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
@@ -1,6 +1,9 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
+
+from erpnext.stock.stock_balance import get_indented_qty, update_bin_qty
+
def execute():
bin_details = frappe.db.sql("""
@@ -10,4 +13,4 @@
for entry in bin_details:
update_bin_qty(entry.get("item_code"), entry.get("warehouse"), {
"indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse"))
- })
\ No newline at end of file
+ })
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 d1446b3..fba4118 100644
--- a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
+++ b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.regional.india.setup import make_custom_fields
+
def execute():
frappe.reload_doc("accounts", "doctype", "tax_category")
@@ -11,4 +12,4 @@
if frappe.db.exists("Custom Field", "Company-bank_remittance_section"):
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])
\ No newline at end of file
+ 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 7859606..f6a1984 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
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import getdate, today
+
def execute():
''' Delete leave ledger entry created
@@ -25,4 +26,4 @@
WHERE
transaction_type = 'Leave Application'
AND transaction_name in (%s) ''' % (', '.join(['%s'] * len(leave_application_list))), #nosec
- tuple(leave_application_list))
\ No newline at end of file
+ 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 24286dc..6fa1c04 100644
--- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
"""Delete duplicate leave ledger entries of type allocation created."""
frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry')
@@ -43,4 +45,4 @@
AND is_carry_forward = %s
AND from_date = %s
AND to_date = %s
- ''', tuple(d))
\ No newline at end of file
+ ''', tuple(d))
diff --git a/erpnext/patches/v12_0/remove_patient_medical_record_page.py b/erpnext/patches/v12_0/remove_patient_medical_record_page.py
index 904bfe4..bf71c48 100644
--- a/erpnext/patches/v12_0/remove_patient_medical_record_page.py
+++ b/erpnext/patches/v12_0/remove_patient_medical_record_page.py
@@ -1,7 +1,9 @@
# Copyright (c) 2019
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.delete_doc("Page", "medical_record")
diff --git a/erpnext/patches/v12_0/rename_account_type_doctype.py b/erpnext/patches/v12_0/rename_account_type_doctype.py
index ffb4e93..27357a8 100644
--- a/erpnext/patches/v12_0/rename_account_type_doctype.py
+++ b/erpnext/patches/v12_0/rename_account_type_doctype.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
+
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')
\ No newline at end of file
+ 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 4230cb8..7e02fff 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
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
''' Change the fieldname from bank_account_no to bank_account '''
if not frappe.get_meta("Journal Entry Account").has_field("bank_account"):
@@ -14,4 +16,4 @@
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'):
- rename_field("Journal Entry Account", "bank_account_no", "bank_account")
\ No newline at end of file
+ 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 2efa854..5c79ce2 100644
--- a/erpnext/patches/v12_0/rename_bank_reconciliation.py
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.table_exists("Bank Reconciliation"):
frappe.rename_doc('DocType', 'Bank Reconciliation', 'Bank Clearance', force=True)
diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
index 978b1c9..629cd5b 100644
--- a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
@@ -3,6 +3,7 @@
import frappe
+
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
if count == 0:
diff --git a/erpnext/patches/v12_0/rename_lost_reason_detail.py b/erpnext/patches/v12_0/rename_lost_reason_detail.py
index d0dc356..337302a 100644
--- a/erpnext/patches/v12_0/rename_lost_reason_detail.py
+++ b/erpnext/patches/v12_0/rename_lost_reason_detail.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists("DocType", "Lost Reason Detail"):
frappe.reload_doc("crm", "doctype", "opportunity_lost_reason")
@@ -15,4 +17,4 @@
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""")
- frappe.delete_doc("DocType", "Lost Reason Detail")
\ No newline at end of file
+ frappe.delete_doc("DocType", "Lost Reason Detail")
diff --git a/erpnext/patches/v12_0/rename_mws_settings_fields.py b/erpnext/patches/v12_0/rename_mws_settings_fields.py
index e08e376..d5bf38d 100644
--- a/erpnext/patches/v12_0/rename_mws_settings_fields.py
+++ b/erpnext/patches/v12_0/rename_mws_settings_fields.py
@@ -3,6 +3,7 @@
import frappe
+
def execute():
count = frappe.db.sql("SELECT COUNT(*) FROM `tabSingles` WHERE doctype='Amazon MWS Settings' AND field='enable_sync';")[0][0]
if count == 0:
diff --git a/erpnext/patches/v12_0/rename_pos_closing_doctype.py b/erpnext/patches/v12_0/rename_pos_closing_doctype.py
index 0577f81..e6fb1f3 100644
--- a/erpnext/patches/v12_0/rename_pos_closing_doctype.py
+++ b/erpnext/patches/v12_0/rename_pos_closing_doctype.py
@@ -1,16 +1,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
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)
-
+
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)
@@ -22,4 +24,4 @@
frappe.delete_doc("DocType", "POS Closing Voucher")
frappe.delete_doc("DocType", "POS Closing Voucher Taxes")
frappe.delete_doc("DocType", "POS Closing Voucher Details")
- frappe.delete_doc("DocType", "POS Closing Voucher Invoices")
\ No newline at end of file
+ frappe.delete_doc("DocType", "POS Closing Voucher Invoices")
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 b9ad622..4bf3840 100644
--- a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
+++ b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
doctypes = {
diff --git a/erpnext/patches/v12_0/rename_tolerance_fields.py b/erpnext/patches/v12_0/rename_tolerance_fields.py
index aa2fff4..ca2427b 100644
--- a/erpnext/patches/v12_0/rename_tolerance_fields.py
+++ b/erpnext/patches/v12_0/rename_tolerance_fields.py
@@ -1,6 +1,7 @@
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc("stock", "doctype", "item")
frappe.reload_doc("stock", "doctype", "stock_settings")
@@ -12,4 +13,4 @@
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)
- frappe.db.sql("update tabItem set over_billing_allowance=over_delivery_receipt_allowance")
\ No newline at end of file
+ frappe.db.sql("update tabItem set over_billing_allowance=over_delivery_receipt_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 09fc4c1..ff332f7 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
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.db.sql("""UPDATE `tabUser` SET `home_settings` = REPLACE(`home_settings`, 'Accounting', 'Accounts')""")
- frappe.cache().delete_key('home_settings')
\ No newline at end of file
+ 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 13e935b..5150430 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
warehouse_perm = frappe.get_all("User Permission",
fields=["count(*) as p_count", "is_default", "user"], filters={"allow": "Warehouse"}, group_by="user")
@@ -66,6 +68,3 @@
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 85202bf..b76e34a 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
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doc('selling', 'doctype', 'sales_order_item', force=True)
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 5ee75be..849e96e 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "accounts_settings")
- frappe.db.set_value("Accounts Settings", None, "automatically_process_deferred_accounting_entry", 1)
\ No newline at end of file
+ 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 8ba0d79..d3045a1 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
@@ -1,8 +1,10 @@
import frappe
+
+
def execute():
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
- """)
\ No newline at end of file
+ """)
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 4d4fc7c..e363c26 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
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
from frappe.utils import cint
@@ -10,8 +11,8 @@
if frappe.db.exists("DocType", "Asset Settings"):
frappe.reload_doctype("Asset Category")
cwip_value = frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting")
-
+
frappe.db.sql("""UPDATE `tabAsset Category` SET enable_cwip_accounting = %s""", cint(cwip_value))
frappe.db.sql("""DELETE FROM `tabSingles` where doctype = 'Asset Settings'""")
- frappe.delete_doc_if_exists("DocType", "Asset Settings")
\ No newline at end of file
+ frappe.delete_doc_if_exists("DocType", "Asset Settings")
diff --git a/erpnext/patches/v12_0/set_default_homepage_type.py b/erpnext/patches/v12_0/set_default_homepage_type.py
index 241e4b9..1e4333a 100644
--- a/erpnext/patches/v12_0/set_default_homepage_type.py
+++ b/erpnext/patches/v12_0/set_default_homepage_type.py
@@ -1,4 +1,5 @@
import frappe
+
def execute():
- frappe.db.set_value('Homepage', 'Homepage', 'hero_section_based_on', 'Default')
\ No newline at end of file
+ frappe.db.set_value('Homepage', 'Homepage', 'hero_section_based_on', 'Default')
diff --git a/erpnext/patches/v12_0/set_default_payroll_based_on.py b/erpnext/patches/v12_0/set_default_payroll_based_on.py
index 04b54a6..85112f2 100644
--- a/erpnext/patches/v12_0/set_default_payroll_based_on.py
+++ b/erpnext/patches/v12_0/set_default_payroll_based_on.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("hr", "doctype", "hr_settings")
- frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave")
\ No newline at end of file
+ frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave")
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 a996a69..49b3bff 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from six import iteritems
+
def execute():
frappe.reload_doctype('Landed Cost Taxes and Charges')
@@ -30,4 +32,4 @@
s.docstatus = 1
AND s.company = %s
AND t.parent = s.name
- """, (account, company))
\ No newline at end of file
+ """, (account, company))
diff --git a/erpnext/patches/v12_0/set_gst_category.py b/erpnext/patches/v12_0/set_gst_category.py
index 55bbdee..094e2a3 100644
--- a/erpnext/patches/v12_0/set_gst_category.py
+++ b/erpnext/patches/v12_0/set_gst_category.py
@@ -1,6 +1,8 @@
import frappe
+
from erpnext.regional.india.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -48,5 +50,3 @@
frappe.db.sql(""" UPDATE `tab{doctype}` t1, `tabAddress` t2, `tabDynamic Link` t3 SET t1.gst_category = "Overseas"
where t3.link_name = t1.name and t3.parent = t2.name and t2.country != 'India' """.format(doctype=doctype)) #nosec
-
-
diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py
index a6011c4..a991b3c 100644
--- a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py
+++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py
@@ -2,11 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
from erpnext.regional.italy.setup import add_permissions
+
def execute():
countries = frappe.get_all("Company", fields="country")
countries = [country["country"] for country in countries]
if "Italy" in countries:
- add_permissions()
\ No newline at end of file
+ add_permissions()
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 70ca6b2..fada5f0 100644
--- a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
+++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt
-from erpnext.stock.get_item_details import get_conversion_factor
+
def execute():
frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item')
@@ -13,4 +13,4 @@
SET
stock_uom = uom,
conversion_factor = 1,
- stock_qty = qty""")
\ No newline at end of file
+ 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 fafbec6..f879295 100644
--- a/erpnext/patches/v12_0/set_payment_entry_status.py
+++ b/erpnext/patches/v12_0/set_payment_entry_status.py
@@ -1,9 +1,10 @@
import frappe
+
def execute():
frappe.reload_doctype("Payment Entry")
frappe.db.sql("""update `tabPayment Entry` set status = CASE
WHEN docstatus = 1 THEN 'Submitted'
WHEN docstatus = 2 THEN 'Cancelled'
ELSE 'Draft'
- END;""")
\ No newline at end of file
+ END;""")
diff --git a/erpnext/patches/v12_0/set_permission_einvoicing.py b/erpnext/patches/v12_0/set_permission_einvoicing.py
index e223510..01cab14 100644
--- a/erpnext/patches/v12_0/set_permission_einvoicing.py
+++ b/erpnext/patches/v12_0/set_permission_einvoicing.py
@@ -1,7 +1,9 @@
import frappe
-from erpnext.regional.italy.setup import make_custom_fields
from frappe.permissions import add_permission, update_permission_property
+from erpnext.regional.italy.setup import make_custom_fields
+
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'Italy'})
diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py
index a5490ef..6d7d099 100644
--- a/erpnext/patches/v12_0/set_priority_for_support.py
+++ b/erpnext/patches/v12_0/set_priority_for_support.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doc("support", "doctype", "issue_priority")
frappe.reload_doc("support", "doctype", "service_level_priority")
@@ -81,4 +82,4 @@
doc.flags.ignore_validate = True
doc.save(ignore_permissions=True)
except frappe.db.TableMissingError:
- frappe.reload_doc("support", "doctype", "service_level_agreement")
\ No newline at end of file
+ frappe.reload_doc("support", "doctype", "service_level_agreement")
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 0702673..9c851dd 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
@@ -1,7 +1,8 @@
import frappe
-from frappe.utils import flt
+
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
+
def execute():
frappe.reload_doctype('Sales Order Item')
frappe.reload_doctype('Sales Order')
@@ -11,4 +12,4 @@
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)
\ No newline at end of file
+ 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 bae1e28..248d335 100644
--- a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
+++ b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("manufacturing", "doctype", "workstation")
frappe.db.sql(""" UPDATE `tabWorkstation`
- SET production_capacity = 1 """)
\ No newline at end of file
+ SET production_capacity = 1 """)
diff --git a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
index e54c7f3..73c6ce8 100644
--- a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
+++ b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item")
if not frappe.db.a_row_exists("Hub Tracked Item"):
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 52c9a2d..cad947f 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
@@ -1,7 +1,10 @@
from __future__ import unicode_literals
-import frappe
+
from collections import defaultdict
+import frappe
+
+
def execute():
frappe.reload_doc('stock', 'doctype', 'delivery_note_item', force=True)
diff --git a/erpnext/patches/v12_0/set_quotation_status.py b/erpnext/patches/v12_0/set_quotation_status.py
index 64a9080..bb17276 100644
--- a/erpnext/patches/v12_0/set_quotation_status.py
+++ b/erpnext/patches/v12_0/set_quotation_status.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.db.sql(""" UPDATE `tabQuotation` set status = 'Open'
- where docstatus = 1 and status = 'Submitted' """)
\ No newline at end of file
+ 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 88c3e2e..f8b510e 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
purchase_receipts = frappe.db.sql("""
SELECT
diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py
index 3b5f5ef..9a05e70 100644
--- a/erpnext/patches/v12_0/set_serial_no_status.py
+++ b/erpnext/patches/v12_0/set_serial_no_status.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.utils import getdate, nowdate
+
def execute():
frappe.reload_doc('stock', 'doctype', 'serial_no')
diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py
index dbd7e5a..1b4955a 100644
--- a/erpnext/patches/v12_0/set_task_status.py
+++ b/erpnext/patches/v12_0/set_task_status.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doctype('Task')
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 63ca540..21ee23b 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
@@ -5,7 +5,8 @@
import frappe
+
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' """)
\ No newline at end of file
+ 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 4a6e228..72d5521 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("buying", "doctype", "supplier_quotation")
frappe.db.sql("""UPDATE `tabSupplier Quotation`
diff --git a/erpnext/patches/v12_0/setup_einvoice_fields.py b/erpnext/patches/v12_0/setup_einvoice_fields.py
deleted file mode 100644
index 2474bc3..0000000
--- a/erpnext/patches/v12_0/setup_einvoice_fields.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from erpnext.regional.india.setup import add_permissions, add_print_formats
-
-def execute():
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company:
- return
-
- frappe.reload_doc("custom", "doctype", "custom_field")
- frappe.reload_doc("regional", "doctype", "e_invoice_settings")
- custom_fields = {
- 'Sales Invoice': [
- dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1,
- depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
-
- dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='irn', no_copy=1, print_hide=1),
-
- dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
-
- dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
- depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
-
- dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
- depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
-
- dict(fieldname='signed_einvoice', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='signed_qr_code', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, no_copy=1, print_hide=1, read_only=1)
- ]
- }
- create_custom_fields(custom_fields, update=True)
- add_permissions()
- add_print_formats()
-
- einvoice_cond = 'in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category)'
- t = {
- 'mode_of_transport': [{'default': None}],
- 'distance': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.transporter'}],
- 'gst_vehicle_type': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}],
- 'lr_date': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}],
- 'lr_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}],
- 'vehicle_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}],
- 'ewaybill': [
- {'read_only_depends_on': 'eval:doc.irn && doc.ewaybill'},
- {'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)'}
- ]
- }
-
- for field, conditions in t.items():
- for c in conditions:
- [(prop, value)] = c.items()
- frappe.db.set_value('Custom Field', { 'fieldname': field }, prop, value)
diff --git a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
deleted file mode 100644
index 2319c17..0000000
--- a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- company = frappe.get_all('Company', filters = {'country': 'India'})
- if not company:
- return
-
- irn_cancelled_field = frappe.db.exists('Custom Field', {'dt': 'Sales Invoice', 'fieldname': 'irn_cancelled'})
- if irn_cancelled_field:
- frappe.db.set_value('Custom Field', irn_cancelled_field, 'depends_on', 'eval: doc.irn')
- frappe.db.set_value('Custom Field', irn_cancelled_field, 'read_only', 0)
diff --git a/erpnext/patches/v12_0/stock_entry_enhancements.py b/erpnext/patches/v12_0/stock_entry_enhancements.py
index 847d928..b99501d 100644
--- a/erpnext/patches/v12_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v12_0/stock_entry_enhancements.py
@@ -3,9 +3,11 @@
from __future__ import unicode_literals
+
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
def execute():
create_stock_entry_types()
@@ -49,4 +51,4 @@
`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
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v12_0/unhide_cost_center_field.py b/erpnext/patches/v12_0/unhide_cost_center_field.py
index 6005ab7..823dd22 100644
--- a/erpnext/patches/v12_0/unhide_cost_center_field.py
+++ b/erpnext/patches/v12_0/unhide_cost_center_field.py
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
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'
- """)
\ No newline at end of file
+ """)
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 b8efb21..61c4c60 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
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
diff --git a/erpnext/patches/v12_0/update_address_template_for_india.py b/erpnext/patches/v12_0/update_address_template_for_india.py
index 0d582da..d41aae2 100644
--- a/erpnext/patches/v12_0/update_address_template_for_india.py
+++ b/erpnext/patches/v12_0/update_address_template_for_india.py
@@ -2,9 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
from erpnext.regional.address_template.setup import set_up_address_templates
+
def execute():
if frappe.db.get_value('Company', {'country': 'India'}, 'name'):
address_template = frappe.db.get_value('Address Template', 'India', 'template')
diff --git a/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py b/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py
deleted file mode 100644
index 91931ee..0000000
--- a/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py
+++ /dev/null
@@ -1,7 +0,0 @@
-import frappe
-
-def execute():
- job = frappe.db.exists('Scheduled Job Type', 'patient_appointment.send_appointment_reminder')
- if job:
- method = 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder'
- frappe.db.set_value('Scheduled Job Type', job, 'method', method)
\ No newline at end of file
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 309ae4c..2834075 100644
--- a/erpnext/patches/v12_0/update_bom_in_so_mr.py
+++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("stock", "doctype", "material_request_item")
frappe.reload_doc("selling", "doctype", "sales_order_item")
@@ -16,4 +18,4 @@
WHERE
child_doc.item_code = item.name and child_doc.docstatus < 2
and item.default_bom is not null and item.default_bom != '' {cond}
- """.format(doc = doctype, cond = condition))
\ No newline at end of file
+ """.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 3484872..60ad325 100644
--- a/erpnext/patches/v12_0/update_due_date_in_gle.py
+++ b/erpnext/patches/v12_0/update_due_date_in_gle.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "gl_entry")
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 db71a73..208076b 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.utils import add_days, getdate, today
+
def execute():
if frappe.db.exists('DocType', 'Email Campaign'):
email_campaign = frappe.get_all('Email Campaign')
@@ -21,4 +23,4 @@
elif end_date >= today_date:
doc.db_set("status", "In Progress")
elif end_date < today_date:
- doc.db_set("status", "Completed")
\ No newline at end of file
+ doc.db_set("status", "Completed")
diff --git a/erpnext/patches/v12_0/update_ewaybill_field_position.py b/erpnext/patches/v12_0/update_ewaybill_field_position.py
index c0230c4..520b5d0 100644
--- a/erpnext/patches/v12_0/update_ewaybill_field_position.py
+++ b/erpnext/patches/v12_0/update_ewaybill_field_position.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.regional.india.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -25,4 +26,4 @@
'translatable': 0
})
- ewaybill_field.save()
\ No newline at end of file
+ ewaybill_field.save()
diff --git a/erpnext/patches/v12_0/update_gst_category.py b/erpnext/patches/v12_0/update_gst_category.py
index 963edad..bee8919 100644
--- a/erpnext/patches/v12_0/update_gst_category.py
+++ b/erpnext/patches/v12_0/update_gst_category.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -16,4 +18,4 @@
frappe.db.sql(""" UPDATE `tabPurchase Invoice` set gst_category = 'Unregistered'
where gst_category = 'Registered Regular'
and ifnull(supplier_gstin, '')=''
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
index d06c571..bfad3dd 100644
--- a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
+++ b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
@@ -1,7 +1,8 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
+from frappe.modules import get_doctype_module, scrub
field_rename_map = {
'Healthcare Settings': [
@@ -134,4 +135,4 @@
status = (CASE WHEN visited >= max_visits THEN 'Completed'
ELSE 'Pending'
END)
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v12_0/update_is_cancelled_field.py b/erpnext/patches/v12_0/update_is_cancelled_field.py
index 0b2e827..3e7c37f 100644
--- a/erpnext/patches/v12_0/update_is_cancelled_field.py
+++ b/erpnext/patches/v12_0/update_is_cancelled_field.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
try:
frappe.db.sql("UPDATE `tabStock Ledger Entry` SET is_cancelled = 0 where is_cancelled in ('', NULL, 'No')")
@@ -11,5 +13,5 @@
frappe.reload_doc("stock", "doctype", "stock_ledger_entry")
frappe.reload_doc("stock", "doctype", "serial_no")
- except:
- pass
\ No newline at end of file
+ except Exception:
+ pass
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 f749699..3ad983d 100644
--- a/erpnext/patches/v12_0/update_item_tax_template_company.py
+++ b/erpnext/patches/v12_0/update_item_tax_template_company.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'item_tax_template')
@@ -10,4 +12,4 @@
for tax in doc.taxes:
doc.company = frappe.get_value('Account', tax.tax_type, 'company')
break
- doc.save()
\ No newline at end of file
+ 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 e4dcecd..09e2319 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
@@ -1,6 +1,11 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_doctypes_with_dimensions
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_doctypes_with_dimensions,
+)
+
def execute():
accounting_dimensions = frappe.db.sql("""select fieldname from
@@ -14,4 +19,4 @@
SET owner = 'Administrator'
WHERE fieldname = %s
AND dt IN (%s)""" % #nosec
- ('%s', ', '.join(['%s']* len(doclist))), tuple([dimension.fieldname] + doclist))
\ No newline at end of file
+ ('%s', ', '.join(['%s']* len(doclist))), 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 f5e7b94..e4c1008 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
@@ -1,8 +1,11 @@
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import getdate, flt
+from frappe.utils import getdate
+
from erpnext.setup.utils import get_exchange_rate
+
def execute():
frappe.reload_doc("manufacturing", "doctype", "bom")
frappe.reload_doc("manufacturing", "doctype", "bom_item")
@@ -28,4 +31,4 @@
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)
\ No newline at end of file
+ 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 3a8cd43..4ff3925 100644
--- a/erpnext/patches/v12_0/update_price_or_product_discount.py
+++ b/erpnext/patches/v12_0/update_price_or_product_discount.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "pricing_rule")
diff --git a/erpnext/patches/v12_0/update_pricing_rule_fields.py b/erpnext/patches/v12_0/update_pricing_rule_fields.py
index 985613a..6f102e9 100644
--- a/erpnext/patches/v12_0/update_pricing_rule_fields.py
+++ b/erpnext/patches/v12_0/update_pricing_rule_fields.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
parentfield = {
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 7450e9c..25cf6b9 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
@@ -1,6 +1,8 @@
import frappe
+
from erpnext.regional.india import states
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -19,4 +21,4 @@
gst_state = 'Dadra and Nagar Haveli and Daman and Diu',
gst_state_number = 26
WHERE gst_state = 'Daman and Diu'
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py
index b5a20aa..7c7477a 100644
--- a/erpnext/patches/v12_0/update_uom_conversion_factor.py
+++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py
@@ -1,5 +1,7 @@
from __future__ import unicode_literals
-import frappe, json
+
+import frappe
+
def execute():
from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data
@@ -8,4 +10,4 @@
frappe.reload_doc("setup", "doctype", "UOM")
frappe.reload_doc("stock", "doctype", "UOM Category")
- add_uom_data()
\ No newline at end of file
+ add_uom_data()
diff --git a/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
deleted file mode 100644
index 01a4ae0..0000000
--- a/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import frappe
-
-def execute():
- frappe.reload_doc('custom', 'doctype', 'custom_field')
- 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', '')
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 73ff1ca..b34b5c1 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
@@ -1,14 +1,19 @@
# Copyright (c) 2020, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
import frappe
-from erpnext.regional.south_africa.setup import make_custom_fields, add_permissions
+
+from erpnext.regional.south_africa.setup import add_permissions, make_custom_fields
+
def execute():
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')
+
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
new file mode 100644
index 0000000..5e8a27f
--- /dev/null
+++ b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
@@ -0,0 +1,37 @@
+from __future__ import unicode_literals
+
+import os
+
+import frappe
+from frappe import _
+
+
+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'))
+
+ 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'))
+
+ 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.save()
diff --git a/erpnext/patches/v13_0/add_doctype_to_sla.py b/erpnext/patches/v13_0/add_doctype_to_sla.py
index e2c7fd2..7252b3e 100644
--- a/erpnext/patches/v13_0/add_doctype_to_sla.py
+++ b/erpnext/patches/v13_0/add_doctype_to_sla.py
@@ -6,6 +6,7 @@
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc('support', 'doctype', 'sla_fulfilled_on_status')
frappe.reload_doc('support', 'doctype', 'service_level_agreement')
@@ -18,4 +19,4 @@
agreement.apply_sla_for_resolution = 1
agreement.append('sla_fulfilled_on', {'status': 'Resolved'})
agreement.append('sla_fulfilled_on', {'status': 'Closed'})
- agreement.save()
\ No newline at end of file
+ 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 0d8109c..bd18b9b 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
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
import frappe
-from frappe.utils import cstr, flt, cint
-from erpnext.stock.stock_ledger import make_sl_entries
+from frappe.utils import cint, cstr, flt
+
from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
+from erpnext.stock.stock_ledger import make_sl_entries
+
def execute():
if not frappe.db.has_column('Work Order', 'has_batch_no'):
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 5ed9040..f029f75 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
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
+
import frappe
-from frappe.custom.doctype.property_setter.property_setter import make_property_setter, delete_property_setter
+
def execute():
frappe.reload_doc("projects", "doctype", "project")
@@ -10,4 +11,3 @@
naming_series = 'PROJ-.####'
WHERE
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 1c60b18..15b7c71 100644
--- a/erpnext/patches/v13_0/add_po_to_global_search.py
+++ b/erpnext/patches/v13_0/add_po_to_global_search.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
diff --git a/erpnext/patches/v13_0/add_standard_navbar_items.py b/erpnext/patches/v13_0/add_standard_navbar_items.py
index d05b258..699c480 100644
--- a/erpnext/patches/v13_0/add_standard_navbar_items.py
+++ b/erpnext/patches/v13_0/add_standard_navbar_items.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
# import frappe
from erpnext.setup.install import add_standard_navbar_items
+
def execute():
# Add standard navbar items for ERPNext in Navbar Settings
add_standard_navbar_items()
diff --git a/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py b/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py
index 7de9fa1..07d1cee 100644
--- a/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py
+++ b/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doctype("Buying Settings")
buying_settings = frappe.get_single("Buying Settings")
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 605a29e..5a0320a 100644
--- a/erpnext/patches/v13_0/change_default_pos_print_format.py
+++ b/erpnext/patches/v13_0/change_default_pos_print_format.py
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.db.sql(
"""UPDATE `tabPOS Profile` profile
SET profile.`print_format` = 'POS Invoice'
- WHERE profile.`print_format` = 'Point of Sale'""")
\ No newline at end of file
+ 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 c92d52d..aac87ba 100644
--- a/erpnext/patches/v13_0/check_is_income_tax_component.py
+++ b/erpnext/patches/v13_0/check_is_income_tax_component.py
@@ -3,9 +3,12 @@
from __future__ import unicode_literals
-import frappe, erpnext
+import frappe
+
+import erpnext
from erpnext.regional.india.setup import setup
+
def execute():
doctypes = ['salary_component',
@@ -19,10 +22,10 @@
]
for doctype in doctypes:
- frappe.reload_doc('Payroll', 'doctype', doctype)
+ frappe.reload_doc('Payroll', 'doctype', doctype, force=True)
- reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
+ 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)
@@ -43,4 +46,4 @@
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")
\ No newline at end of file
+ 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 289b6a7..66ac62f 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('stock', 'doctype', 'quality_inspection_parameter')
@@ -20,4 +22,4 @@
"doctype": "Quality Inspection Parameter",
"parameter": parameter,
"description": parameter
- }).insert(ignore_permissions=True)
\ No newline at end of file
+ }).insert(ignore_permissions=True)
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
new file mode 100644
index 0000000..4450108
--- /dev/null
+++ b/erpnext/patches/v13_0/create_accounting_dimensions_in_pos_doctypes.py
@@ -0,0 +1,42 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+
+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)
+
+ 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 ["POS Invoice", "POS Invoice Item"]:
+
+ field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+ if field:
+ continue
+ meta = frappe.get_meta(doctype, cached=False)
+ fieldnames = [d.fieldname for d in meta.get("fields")]
+
+ df = {
+ "fieldname": d.fieldname,
+ "label": d.label,
+ "fieldtype": "Link",
+ "options": d.document_type,
+ "insert_after": insert_after_field
+ }
+
+ if df['fieldname'] not in fieldnames:
+ create_custom_field(doctype, df)
+ frappe.clear_cache(doctype=doctype)
+
+ count += 1
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
new file mode 100644
index 0000000..313b0e9
--- /dev/null
+++ b/erpnext/patches/v13_0/create_custom_field_for_finance_book.py
@@ -0,0 +1,21 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ custom_field = {
+ '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%.'
+ }
+ ]
+ }
+ create_custom_fields(custom_field, update=1)
diff --git a/erpnext/patches/v13_0/create_gst_payment_entry_fields.py b/erpnext/patches/v13_0/create_gst_payment_entry_fields.py
new file mode 100644
index 0000000..7e6d67c
--- /dev/null
+++ b/erpnext/patches/v13_0/create_gst_payment_entry_fields.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+ frappe.reload_doc('accounts', 'doctype', 'advance_taxes_and_charges')
+ frappe.reload_doc('accounts', 'doctype', 'payment_entry')
+
+ custom_fields = {
+ 'Payment Entry': [
+ dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break', insert_after='deductions',
+ print_hide=1, collapsible=1),
+ dict(fieldname='company_address', label='Company Address', fieldtype='Link', insert_after='gst_section',
+ print_hide=1, options='Address'),
+ dict(fieldname='company_gstin', label='Company GSTIN',
+ fieldtype='Data', insert_after='company_address',
+ fetch_from='company_address.gstin', print_hide=1, read_only=1),
+ dict(fieldname='place_of_supply', label='Place of Supply',
+ fieldtype='Data', insert_after='company_gstin',
+ print_hide=1, read_only=1),
+ dict(fieldname='customer_address', label='Customer Address', fieldtype='Link', insert_after='place_of_supply',
+ print_hide=1, options='Address', depends_on = 'eval:doc.party_type == "Customer"'),
+ dict(fieldname='customer_gstin', label='Customer GSTIN',
+ fieldtype='Data', insert_after='customer_address',
+ fetch_from='customer_address.gstin', print_hide=1, read_only=1)
+ ]
+ }
+
+ create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py b/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py
deleted file mode 100644
index 585e540..0000000
--- a/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from erpnext.domains.healthcare import data
-
-def execute():
- if 'Healthcare' not in frappe.get_active_domains():
- return
-
- if data['custom_fields']:
- create_custom_fields(data['custom_fields'])
\ No newline at end of file
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
index 90dc0e2..b1b5c24 100644
--- 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
@@ -5,6 +5,7 @@
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)
@@ -74,6 +75,3 @@
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 59b2e49..3bfa78f 100644
--- a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
+++ b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
@@ -4,8 +4,10 @@
from __future__ import unicode_literals
import frappe
+
from erpnext.regional.united_arab_emirates.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': ['in', ['Saudi Arabia', 'United Arab Emirates']]})
if not company:
@@ -15,4 +17,4 @@
frappe.reload_doc('accounts', 'doctype', 'pos_invoice')
frappe.reload_doc('accounts', 'doctype', 'pos_invoice_item')
- make_custom_fields()
\ No newline at end of file
+ make_custom_fields()
diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
new file mode 100644
index 0000000..eee9f11
--- /dev/null
+++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
@@ -0,0 +1,32 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+from erpnext.regional.united_states.setup import add_permissions
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'United States'}, fields=['name'])
+ if not company:
+ return
+
+ frappe.reload_doc("regional", "doctype", "product_tax_category")
+
+ custom_fields = {
+ 'Sales Invoice Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
+ label='Product Tax Category', fetch_from='item_code.product_tax_category'),
+ dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
+ label='Tax Collectable', read_only=1),
+ dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
+ label='Taxable Amount', read_only=1)
+ ],
+ 'Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
+ label='Product Tax Category')
+ ]
+ }
+ create_custom_fields(custom_fields, update=True)
+ add_permissions()
+ frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=True)
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 77a23cf..089bbe3 100644
--- a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
+++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
@@ -6,6 +6,7 @@
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
doctypes = [
"Bank Statement Settings",
diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py
index 8bdc07e..3cb7e12 100644
--- a/erpnext/patches/v13_0/delete_old_purchase_reports.py
+++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py
@@ -5,6 +5,9 @@
import frappe
+from erpnext.accounts.utils import check_and_delete_linked_reports
+
+
def execute():
reports_to_delete = ["Requested Items To Be Ordered",
"Purchase Order Items To Be Received or Billed","Purchase Order Items To Be Received",
@@ -13,6 +16,7 @@
for report in reports_to_delete:
if frappe.db.exists("Report", report):
delete_auto_email_reports(report)
+ check_and_delete_linked_reports(report)
frappe.delete_doc("Report", report)
@@ -20,4 +24,4 @@
""" 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])
\ No newline at end of file
+ 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 0f44865..c9a3666 100644
--- a/erpnext/patches/v13_0/delete_old_sales_reports.py
+++ b/erpnext/patches/v13_0/delete_old_sales_reports.py
@@ -5,12 +5,16 @@
import frappe
+from erpnext.accounts.utils import check_and_delete_linked_reports
+
+
def execute():
reports_to_delete = ["Ordered Items To Be Delivered", "Ordered Items To Be Billed"]
for report in reports_to_delete:
if frappe.db.exists("Report", report):
delete_auto_email_reports(report)
+ check_and_delete_linked_reports(report)
frappe.delete_doc("Report", report)
@@ -18,4 +22,4 @@
""" 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])
\ No newline at end of file
+ frappe.delete_doc("Auto Email Report", auto_email_report[0])
diff --git a/erpnext/patches/v13_0/delete_orphaned_tables.py b/erpnext/patches/v13_0/delete_orphaned_tables.py
index 1d6eebe..1ea22d6 100644
--- a/erpnext/patches/v13_0/delete_orphaned_tables.py
+++ b/erpnext/patches/v13_0/delete_orphaned_tables.py
@@ -6,6 +6,7 @@
import frappe
from frappe.utils import getdate
+
def execute():
frappe.reload_doc('setup', 'doctype', 'transaction_deletion_record')
@@ -28,9 +29,9 @@
def get_child_doctypes_whose_parent_doctypes_were_affected():
parent_doctypes = get_affected_doctypes()
child_doctypes = frappe.get_all(
- 'DocField',
+ 'DocField',
filters={
- 'fieldtype': 'Table',
+ 'fieldtype': 'Table',
'parent':['in', parent_doctypes]
}, pluck='options')
@@ -39,7 +40,7 @@
def get_affected_doctypes():
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)
@@ -66,4 +67,4 @@
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)
\ No newline at end of file
+ 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 94a9fa8..87565f0 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
@@ -1,5 +1,6 @@
import frappe
+
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"])
@@ -9,4 +10,4 @@
frappe.db.sql("""
DELETE FROM `tabReport`
WHERE name = 'Requested Items to Order'
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v13_0/drop_razorpay_payload_column.py b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
index 8980fd0..a7aee52 100644
--- a/erpnext/patches/v13_0/drop_razorpay_payload_column.py
+++ b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists("DocType", "Membership"):
if 'webhook_payload' in frappe.db.get_table_columns("Membership"):
- frappe.db.sql("alter table `tabMembership` drop column webhook_payload")
\ No newline at end of file
+ frappe.db.sql("alter table `tabMembership` drop column webhook_payload")
diff --git a/erpnext/patches/v13_0/einvoicing_deprecation_warning.py b/erpnext/patches/v13_0/einvoicing_deprecation_warning.py
new file mode 100644
index 0000000..e123a55
--- /dev/null
+++ b/erpnext/patches/v13_0/einvoicing_deprecation_warning.py
@@ -0,0 +1,9 @@
+import click
+
+
+def execute():
+ click.secho(
+ "Indian E-Invoicing integration is moved to a separate app and will be removed from ERPNext in version-14.\n"
+ "Please install the app to continue using the integration: https://github.com/frappe/erpnext_gst_compliance",
+ fg="yellow",
+ )
diff --git a/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py b/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py
new file mode 100644
index 0000000..aeb8d8e
--- /dev/null
+++ b/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py
@@ -0,0 +1,76 @@
+from typing import List, NewType
+
+import frappe
+
+StockEntryCode = NewType("StockEntryCode", str)
+
+
+def execute():
+ stock_entry_codes = find_broken_stock_entries()
+
+ for stock_entry_code in stock_entry_codes:
+ patched_stock_entry = patch_additional_cost(stock_entry_code)
+ create_repost_item_valuation(patched_stock_entry)
+
+
+def find_broken_stock_entries() -> List[StockEntryCode]:
+ period_closing_date = frappe.db.get_value(
+ "Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc"
+ )
+
+ stock_entries_to_patch = frappe.db.sql(
+ """
+ select se.name, sum(sed.additional_cost) as item_additional_cost, se.total_additional_costs
+ from `tabStock Entry` se
+ join `tabStock Entry Detail` sed
+ on sed.parent = se.name
+ where
+ se.docstatus = 1 and
+ se.posting_date > %s
+ group by
+ sed.parent
+ having
+ item_additional_cost != se.total_additional_costs
+ """,
+ period_closing_date,
+ as_dict=True,
+ )
+
+ return [d.name for d in stock_entries_to_patch]
+
+
+def patch_additional_cost(code: StockEntryCode):
+ stock_entry = frappe.get_doc("Stock Entry", code)
+ stock_entry.distribute_additional_costs()
+ stock_entry.update_valuation_rate()
+ stock_entry.set_total_incoming_outgoing_value()
+ stock_entry.set_total_amount()
+ stock_entry.db_update()
+ for item in stock_entry.items:
+ item.db_update()
+ return stock_entry
+
+
+def create_repost_item_valuation(stock_entry):
+ from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
+
+ # turn on recalculate flag so reposting corrects the incoming/outgoing rates.
+ frappe.db.set_value(
+ "Stock Ledger Entry",
+ {"voucher_no": stock_entry.name, "actual_qty": (">", 0)},
+ "recalculate_rate",
+ 1,
+ update_modified=False,
+ )
+
+ create_repost_item_valuation_entry(
+ args=frappe._dict(
+ {
+ "posting_date": stock_entry.posting_date,
+ "posting_time": stock_entry.posting_time,
+ "voucher_type": stock_entry.doctype,
+ "voucher_no": stock_entry.name,
+ "company": stock_entry.company,
+ }
+ )
+ )
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 61dc824..e91c1db 100644
--- a/erpnext/patches/v13_0/fix_non_unique_represents_company.py
+++ b/erpnext/patches/v13_0/fix_non_unique_represents_company.py
@@ -1,8 +1,9 @@
import frappe
+
def execute():
frappe.db.sql("""
update tabCustomer
set represents_company = NULL
where represents_company = ''
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
index 11e1e9b..dca43b4 100644
--- a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
+++ b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
@@ -12,7 +12,7 @@
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".
+ stored in a new custom field "debtor_creditor_number".
"""
company_list = frappe.get_all('Company', filters={'country': 'Germany'})
diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py
index 41ab945..86ad188 100644
--- a/erpnext/patches/v13_0/germany_make_custom_fields.py
+++ b/erpnext/patches/v13_0/germany_make_custom_fields.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
+
from erpnext.regional.germany.setup import make_custom_fields
diff --git a/erpnext/patches/v13_0/gst_fields_for_pos_invoice.py b/erpnext/patches/v13_0/gst_fields_for_pos_invoice.py
new file mode 100644
index 0000000..5b790d9
--- /dev/null
+++ b/erpnext/patches/v13_0/gst_fields_for_pos_invoice.py
@@ -0,0 +1,44 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'}, fields=['name'])
+ if not company:
+ return
+
+ hsn_sac_field = 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=1, fetch_if_empty=1)
+ nil_rated_exempt = dict(fieldname='is_nil_exempt', label='Is Nil Rated or Exempted',
+ fieldtype='Check', fetch_from='item_code.is_nil_exempt', insert_after='gst_hsn_code',
+ print_hide=1)
+ is_non_gst = dict(fieldname='is_non_gst', label='Is Non GST',
+ fieldtype='Check', fetch_from='item_code.is_non_gst', insert_after='is_nil_exempt',
+ print_hide=1)
+ taxable_value = 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_gst_fields = [
+ dict(fieldname='billing_address_gstin', label='Billing Address GSTIN',
+ fieldtype='Data', insert_after='customer_address', read_only=1,
+ fetch_from='customer_address.gstin', print_hide=1),
+ dict(fieldname='customer_gstin', label='Customer GSTIN',
+ fieldtype='Data', insert_after='shipping_address_name',
+ fetch_from='shipping_address_name.gstin', print_hide=1),
+ dict(fieldname='place_of_supply', label='Place of Supply',
+ fieldtype='Data', insert_after='customer_gstin',
+ print_hide=1, read_only=1),
+ dict(fieldname='company_gstin', label='Company GSTIN',
+ fieldtype='Data', insert_after='company_address',
+ fetch_from='company_address.gstin', print_hide=1, read_only=1),
+ ]
+
+ custom_fields = {
+ 'POS Invoice': sales_invoice_gst_fields,
+ 'POS Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
+ }
+
+ create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/healthcare_deprecation_warning.py b/erpnext/patches/v13_0/healthcare_deprecation_warning.py
new file mode 100644
index 0000000..c6fba59
--- /dev/null
+++ b/erpnext/patches/v13_0/healthcare_deprecation_warning.py
@@ -0,0 +1,10 @@
+import click
+
+
+def execute():
+
+ click.secho(
+ "Healthcare Module is moved to a separate app and will be removed from ERPNext in version-14.\n"
+ "Please install the app to continue using the module: https://github.com/frappe/healthcare",
+ fg="yellow",
+ )
diff --git a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
index 2549a1e..f297654 100644
--- a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
+++ b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
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 021bb72..e4cb9ae 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
@@ -1,8 +1,9 @@
import frappe
-from frappe import _
-from frappe.utils import getdate, get_time, today
-from erpnext.stock.stock_ledger import update_entries_after
+from frappe.utils import get_time, getdate, today
+
from erpnext.accounts.utils import update_gl_entries_after
+from erpnext.stock.stock_ledger import update_entries_after
+
def execute():
for doctype in ('repost_item_valuation', 'stock_entry_detail', 'purchase_receipt_item',
@@ -67,4 +68,4 @@
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]
\ No newline at end of file
+ INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0]
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 ee77340..43c6c49 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
@@ -5,11 +5,12 @@
import frappe
+
def execute():
'''`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'):
return
@@ -17,4 +18,4 @@
"""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` = '')""")
\ No newline at end of file
+ AND (lpe.`invoice` IS NULL OR lpe.`invoice` = '')""")
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 a9d7883..746e408 100644
--- a/erpnext/patches/v13_0/make_non_standard_user_type.py
+++ b/erpnext/patches/v13_0/make_non_standard_user_type.py
@@ -2,10 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from six import iteritems
+
from erpnext.setup.install import add_non_standard_user_types
+
def execute():
doctype_dict = {
'projects': ['Timesheet'],
@@ -21,4 +24,4 @@
frappe.flags.ignore_select_perm = True
frappe.flags.update_select_perm_after_migrate = True
- add_non_standard_user_types()
\ No newline at end of file
+ add_non_standard_user_types()
diff --git a/erpnext/patches/v13_0/migrate_stripe_api.py b/erpnext/patches/v13_0/migrate_stripe_api.py
new file mode 100644
index 0000000..355421a
--- /dev/null
+++ b/erpnext/patches/v13_0/migrate_stripe_api.py
@@ -0,0 +1,7 @@
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+ frappe.reload_doc("accounts", "doctype", "subscription_plan")
+ rename_field("Subscription Plan", "payment_plan_id", "product_price_id")
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
new file mode 100644
index 0000000..fa8a864
--- /dev/null
+++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
@@ -0,0 +1,49 @@
+from __future__ import unicode_literals
+
+import json
+
+import frappe
+
+
+def execute():
+ frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_advance')
+ frappe.reload_doc('accounts', 'doctype', 'sales_invoice_advance')
+
+ purchase_invoices = frappe.db.sql("""
+ select
+ parenttype as type, parent as name
+ from
+ `tabPurchase Invoice Advance`
+ where
+ ref_exchange_rate = 1
+ and docstatus = 1
+ and ifnull(exchange_gain_loss, '') != ''
+ group by
+ parent
+ """, as_dict=1)
+
+ sales_invoices = frappe.db.sql("""
+ select
+ parenttype as type, parent as name
+ from
+ `tabSales Invoice Advance`
+ where
+ ref_exchange_rate = 1
+ and docstatus = 1
+ and ifnull(exchange_gain_loss, '') != ''
+ group by
+ parent
+ """, as_dict=1)
+
+ if purchase_invoices + sales_invoices:
+ frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
+
+ for invoice in purchase_invoices + sales_invoices:
+ doc = frappe.get_doc(invoice.type, invoice.name)
+ doc.docstatus = 2
+ doc.make_gl_entries()
+ for advance in doc.advances:
+ if advance.ref_exchange_rate == 1:
+ advance.db_set('exchange_gain_loss', 0, False)
+ doc.docstatus = 1
+ doc.make_gl_entries()
\ No newline at end of file
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 833ae2a..9116f58 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
@@ -5,6 +5,7 @@
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'bank_account')
@@ -14,4 +15,4 @@
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, '') = ''""")
\ No newline at end of file
+ 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 4d7c85c..064e8d7 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
@@ -5,6 +5,7 @@
import frappe
+
def execute():
frappe.db.sql("""UPDATE `tabPrint Format`
SET module = 'Payroll'
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 a901064..85d5109 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
@@ -5,6 +5,7 @@
import frappe
+
def execute():
data = frappe.db.sql('''SELECT *
FROM `tabSingles`
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 1a91d21..e73fa7b 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
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.model.utils.rename_field import rename_field
+
def execute():
if not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")):
@@ -86,7 +86,7 @@
try:
employee_other_income.submit()
migrated.append([proof.employee, proof.payroll_period])
- except:
+ except Exception:
pass
if not frappe.db.table_exists("Employee Tax Exemption Declaration"):
@@ -108,5 +108,5 @@
try:
employee_other_income.submit()
- except:
+ except Exception:
pass
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 fde8f86..bb539a7 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
@@ -2,6 +2,7 @@
import frappe
+
def execute():
if not frappe.db.table_exists("Additional Salary"):
return
@@ -51,4 +52,3 @@
and parent = %s
and salary_component = %s
""", (salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]))
-
diff --git a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py b/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
index 0de3728..f2022b2 100644
--- a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
+++ b/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
@@ -3,8 +3,8 @@
from __future__ import unicode_literals
-import frappe
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 53da700..bbe3eb5 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
@@ -1,5 +1,6 @@
import frappe
+
def execute():
"""Remove has_variants and attribute fields from item variant settings."""
frappe.reload_doc("stock", "doctype", "Item Variant Settings")
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
index 491dc82..2a04211 100644
--- a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
+++ b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
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"):
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
index 41c51c3..4aeafea 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
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'],
@@ -41,6 +43,7 @@
rename_field('Opportunity', 'mins_to_first_response', 'first_response_time')
# change fieldtype to duration
+ 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')
@@ -58,6 +61,8 @@
def convert_to_seconds(value, unit):
seconds = 0
+ if value == 0:
+ return seconds
if unit == 'Hours':
seconds = value * 3600
if unit == 'Minutes':
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 48325fc..1d245db 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists('DocType', 'Issue'):
frappe.reload_doc("support", "doctype", "issue")
@@ -17,4 +19,4 @@
status = 'On Hold'
WHERE
status = 'Hold'
- """)
\ No newline at end of file
+ """)
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 3fa09a7..23a782a 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
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
if frappe.db.table_exists("Membership Settings"):
frappe.rename_doc("DocType", "Membership Settings", "Non Profit Settings")
@@ -19,4 +21,4 @@
}
for old_name, new_name in rename_fields_map.items():
- rename_field("Non Profit Settings", old_name, new_name)
\ No newline at end of file
+ rename_field("Non Profit Settings", old_name, new_name)
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
new file mode 100644
index 0000000..813fbd2
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_stop_to_send_birthday_reminders.py
@@ -0,0 +1,24 @@
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+ frappe.reload_doc('hr', 'doctype', 'hr_settings')
+
+ try:
+ # Rename the field
+ 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')
+
+ frappe.db.set_value(
+ 'HR Settings',
+ 'HR Settings',
+ 'send_birthday_reminders',
+ 1 if old_value == 0 else 0
+ )
+
+ except Exception as e:
+ if e.args[0] != 1054:
+ raise
diff --git a/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py b/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py
index 390e217..989f1a0 100644
--- a/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py
+++ b/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists("Page", "point-of-sale"):
- frappe.rename_doc("Page", "pos", "point-of-sale", 1, 1)
\ No newline at end of file
+ frappe.rename_doc("Page", "pos", "point-of-sale", 1, 1)
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 7cb2648..103c791 100644
--- a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
+++ b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
@@ -5,6 +5,7 @@
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "pos_payment_method")
pos_profiles = frappe.get_all("POS Profile")
@@ -23,5 +24,5 @@
pos_payment_method.parentfield = payment_mode.parentfield
pos_payment_method.parenttype = payment_mode.parenttype
pos_payment_method.db_insert()
-
+
frappe.db.sql("""delete from `tabSales Invoice Payment` where parent=%s""", pos_profile.name)
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
new file mode 100644
index 0000000..ba96fdd
--- /dev/null
+++ b/erpnext/patches/v13_0/replace_supplier_item_group_with_party_specific_item.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+ 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:
+ psi = frappe.new_doc("Party Specific Item")
+ psi.party_type = "Supplier"
+ psi.party = item.supplier
+ psi.restrict_based_on = "Item Group"
+ psi.based_on_value = item.item_group
+ psi.insert()
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
new file mode 100644
index 0000000..29343b7
--- /dev/null
+++ b/erpnext/patches/v13_0/reset_clearance_date_for_intracompany_payment_entries.py
@@ -0,0 +1,46 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+
+
+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.
+ """
+
+ 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)
+
+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'
+ )
+
+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"""
+
+ 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'
+ )
+
+ return reconciled_bank_transactions
diff --git a/erpnext/patches/v13_0/set_app_name.py b/erpnext/patches/v13_0/set_app_name.py
index 3f886f1..4a88442 100644
--- a/erpnext/patches/v13_0/set_app_name.py
+++ b/erpnext/patches/v13_0/set_app_name.py
@@ -1,5 +1,5 @@
import frappe
-from frappe import _
+
def execute():
frappe.reload_doctype("System Settings")
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
index a5b93f6..e9396c2 100644
--- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py
+++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
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']
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 66857c4..c744f35 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
@@ -1,7 +1,8 @@
import frappe
+
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)""")
\ No newline at end of file
+ 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
new file mode 100644
index 0000000..e26285e
--- /dev/null
+++ b/erpnext/patches/v13_0/set_operation_time_based_on_operating_cost.py
@@ -0,0 +1,16 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc('manufacturing', 'doctype', 'bom')
+ frappe.reload_doc('manufacturing', 'doctype', 'bom_operation')
+
+ frappe.db.sql('''
+ UPDATE
+ `tabBOM Operation`
+ SET
+ time_in_mins = (operating_cost * 60) / hour_rate
+ 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 edca238..da889e6 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
"""Set the payment gateway account as Email for all the existing payment channel."""
doc_meta = frappe.get_meta("Payment Gateway Account")
@@ -14,4 +16,4 @@
frappe.db.sql("""
UPDATE `tabPayment Gateway Account`
SET `payment_channel` = "Email"
- """)
\ No newline at end of file
+ """)
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 1c576db..8c7f508 100644
--- a/erpnext/patches/v13_0/set_pos_closing_as_failed.py
+++ b/erpnext/patches/v13_0/set_pos_closing_as_failed.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'pos_closing_entry')
- frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'")
\ No newline at end of file
+ frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'")
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
new file mode 100644
index 0000000..9887ad9
--- /dev/null
+++ b/erpnext/patches/v13_0/set_status_in_maintenance_schedule_table.py
@@ -0,0 +1,10 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc("maintenance", "doctype", "Maintenance Schedule Detail")
+ 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 18cad8d..4e3d8f5 100644
--- a/erpnext/patches/v13_0/set_training_event_attendance.py
+++ b/erpnext/patches/v13_0/set_training_event_attendance.py
@@ -1,9 +1,11 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
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'")
\ No newline at end of file
+ frappe.db.sql("update `tabTraining Event Employee` set `is_mandatory` = 1 where `attendance` = 'Mandatory'")
diff --git a/erpnext/patches/v13_0/set_youtube_video_id.py b/erpnext/patches/v13_0/set_youtube_video_id.py
index c3b49eb..1594e52 100644
--- a/erpnext/patches/v13_0/set_youtube_video_id.py
+++ b/erpnext/patches/v13_0/set_youtube_video_id.py
@@ -1,10 +1,13 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.utilities.doctype.video.video import get_id_from_url
+
def execute():
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:
- frappe.db.set_value("Video", video.name, "youtube_video_id", get_id_from_url(video.url))
\ No newline at end of file
+ frappe.db.set_value("Video", video.name, "youtube_video_id", get_id_from_url(video.url))
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 ecc7822..a6a3ff3 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
@@ -1,10 +1,13 @@
from __future__ import unicode_literals
+
import frappe
+
from erpnext.regional.india.setup import add_custom_roles_for_reports
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
- add_custom_roles_for_reports()
\ No newline at end of file
+ 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 833c355..7a2a253 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
@@ -1,6 +1,8 @@
import frappe
+
from erpnext.regional.india.setup import make_custom_fields
+
def execute():
if frappe.get_all('Company', filters = {'country': 'India'}):
make_custom_fields()
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 01fd6a1..01e75a6 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
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('payroll', 'doctype', 'gratuity_rule')
frappe.reload_doc('payroll', 'doctype', 'gratuity_rule_slab')
diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
deleted file mode 100644
index d927524..0000000
--- a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.healthcare.setup import setup_patient_history_settings
-
-def execute():
- if "Healthcare" not in frappe.get_active_domains():
- return
-
- frappe.reload_doc("healthcare", "doctype", "Inpatient Medication Order")
- frappe.reload_doc("healthcare", "doctype", "Therapy Session")
- frappe.reload_doc("healthcare", "doctype", "Clinical Procedure")
- frappe.reload_doc("healthcare", "doctype", "Patient History Settings")
- frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type")
- frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type")
-
- setup_patient_history_settings()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/setup_uae_vat_fields.py b/erpnext/patches/v13_0/setup_uae_vat_fields.py
index 1830bab..d89e052 100644
--- a/erpnext/patches/v13_0/setup_uae_vat_fields.py
+++ b/erpnext/patches/v13_0/setup_uae_vat_fields.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
import frappe
+
from erpnext.regional.united_arab_emirates.setup import setup
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
if not company:
diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py
index 0bdcc9c..5fcd6a3 100644
--- a/erpnext/patches/v13_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v13_0/stock_entry_enhancements.py
@@ -2,24 +2,26 @@
# License: GNU General Public License v3.See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("stock", "doctype", "stock_entry")
if frappe.db.has_column("Stock Entry", "add_to_transit"):
frappe.db.sql("""
- UPDATE `tabStock Entry` SET
+ 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')
@@ -28,4 +30,4 @@
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")
\ No newline at end of file
+ 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
new file mode 100644
index 0000000..fd48c0d
--- /dev/null
+++ b/erpnext/patches/v13_0/trim_sales_invoice_custom_field_length.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2020, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+from erpnext.regional.india.setup import create_custom_fields, get_custom_fields
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'India'})
+ if not company:
+ return
+
+ custom_fields = {
+ 'Sales Invoice': get_custom_fields().get('Sales Invoice')
+ }
+
+ create_custom_fields(custom_fields, update=True)
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 adfa20e..dd87a53 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
@@ -7,6 +7,7 @@
import frappe
from frappe.utils import add_to_date
+
def execute():
frappe.reload_doc("manufacturing", "doctype", "work_order")
frappe.reload_doc("manufacturing", "doctype", "work_order_item")
@@ -38,4 +39,4 @@
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 eae5ff6..dc973a9 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
@@ -1,5 +1,6 @@
import frappe
+
def execute():
""" Correct amount in child table of required items table."""
@@ -7,4 +8,3 @@
frappe.reload_doc("manufacturing", "doctype", "work_order_item")
frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""")
-
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
new file mode 100644
index 0000000..90fb50f
--- /dev/null
+++ b/erpnext/patches/v13_0/update_dates_in_tax_withholding_category.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+ 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'])
+
+ fiscal_year_map = {}
+ 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')
+
+ frappe.db.set_value('Tax Withholding Rate', rate.name, {
+ 'from_date': from_date,
+ 'to_date': to_date
+ })
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_deferred_settings.py b/erpnext/patches/v13_0/update_deferred_settings.py
index a7d8207..5405931 100644
--- a/erpnext/patches/v13_0/update_deferred_settings.py
+++ b/erpnext/patches/v13_0/update_deferred_settings.py
@@ -1,11 +1,13 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
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()
\ No newline at end of file
+ accounts_settings.save()
diff --git a/erpnext/patches/v13_0/update_export_type_for_gst.py b/erpnext/patches/v13_0/update_export_type_for_gst.py
index 3e20212..de57861 100644
--- a/erpnext/patches/v13_0/update_export_type_for_gst.py
+++ b/erpnext/patches/v13_0/update_export_type_for_gst.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
@@ -8,7 +9,7 @@
# Update custom fields
fieldname = frappe.db.get_value('Custom Field', {'dt': 'Customer', 'fieldname': 'export_type'})
if fieldname:
- frappe.db.set_value('Custom Field', fieldname,
+ frappe.db.set_value('Custom Field', fieldname,
{
'default': '',
'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)'
@@ -16,7 +17,7 @@
fieldname = frappe.db.get_value('Custom Field', {'dt': 'Supplier', 'fieldname': 'export_type'})
if fieldname:
- frappe.db.set_value('Custom Field', fieldname,
+ frappe.db.set_value('Custom Field', fieldname,
{
'default': '',
'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas"], doc.gst_category)'
@@ -29,4 +30,4 @@
frappe.db.sql("""
UPDATE `tabSupplier` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas')
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v13_0/update_job_card_details.py b/erpnext/patches/v13_0/update_job_card_details.py
index d4e65c6..a0405e5 100644
--- a/erpnext/patches/v13_0/update_job_card_details.py
+++ b/erpnext/patches/v13_0/update_job_card_details.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("manufacturing", "doctype", "job_card")
frappe.reload_doc("manufacturing", "doctype", "job_card_item")
@@ -13,4 +15,4 @@
SET jc.hour_rate = wo.hour_rate
WHERE
jc.operation_id = wo.name and jc.docstatus < 2 and wo.hour_rate > 0
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v13_0/update_level_in_bom.py b/erpnext/patches/v13_0/update_level_in_bom.py
index 0d03c42..6223500 100644
--- a/erpnext/patches/v13_0/update_level_in_bom.py
+++ b/erpnext/patches/v13_0/update_level_in_bom.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
for document in ["bom", "bom_item", "bom_explosion_item"]:
frappe.reload_doc('manufacturing', 'doctype', document)
diff --git a/erpnext/patches/v13_0/update_member_email_address.py b/erpnext/patches/v13_0/update_member_email_address.py
index 4056f84..769658e 100644
--- a/erpnext/patches/v13_0/update_member_email_address.py
+++ b/erpnext/patches/v13_0/update_member_email_address.py
@@ -2,9 +2,11 @@
# MIT License. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
"""add value to email_id column from email"""
diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py
index 8cf09aa..b01a877 100644
--- a/erpnext/patches/v13_0/update_old_loans.py
+++ b/erpnext/patches/v13_0/update_old_loans.py
@@ -1,12 +1,18 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import nowdate, flt
-from erpnext.accounts.doctype.account.test_account import create_account
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
-from erpnext.loan_management.doctype.loan.loan import make_repayment_entry
-from erpnext.loan_management.doctype.loan_repayment.loan_repayment import get_accrued_interest_entries
from frappe.model.naming import make_autoname
+from frappe.utils import flt, nowdate
+
+from erpnext.accounts.doctype.account.test_account import create_account
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+ get_accrued_interest_entries,
+)
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
+ process_loan_interest_accrual_for_term_loans,
+)
+
def execute():
diff --git a/erpnext/patches/v13_0/update_payment_terms_outstanding.py b/erpnext/patches/v13_0/update_payment_terms_outstanding.py
index 4816b40..c9e920b 100644
--- a/erpnext/patches/v13_0/update_payment_terms_outstanding.py
+++ b/erpnext/patches/v13_0/update_payment_terms_outstanding.py
@@ -2,8 +2,10 @@
# MIT License. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "Payment Schedule")
if frappe.db.count('Payment Schedule'):
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 262e38d..71fe9ed 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
@@ -2,8 +2,10 @@
# MIT License. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("accounts", "doctype", "POS Invoice Merge Log")
frappe.reload_doc("accounts", "doctype", "POS Closing Entry")
diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py
index 8cc27d2..f0d0a5a 100644
--- a/erpnext/patches/v13_0/update_project_template_tasks.py
+++ b/erpnext/patches/v13_0/update_project_template_tasks.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("projects", "doctype", "project_template")
frappe.reload_doc("projects", "doctype", "project_template_task")
@@ -44,4 +46,4 @@
"task": tsk.name,
"subject": tsk.subject
})
- template.save()
\ No newline at end of file
+ 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 792118f..8407502 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
@@ -2,8 +2,10 @@
# MIT License. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc("hr", "doctype", "employee")
@@ -12,4 +14,3 @@
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
new file mode 100644
index 0000000..f4a4810
--- /dev/null
+++ b/erpnext/patches/v13_0/update_recipient_email_digest.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2020, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+
+
+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()
diff --git a/erpnext/patches/v13_0/update_response_by_variance.py b/erpnext/patches/v13_0/update_response_by_variance.py
index ef4d976..444ec9e 100644
--- a/erpnext/patches/v13_0/update_response_by_variance.py
+++ b/erpnext/patches/v13_0/update_response_by_variance.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
if frappe.db.exists('DocType', 'Issue') and frappe.db.count('Issue'):
invalid_issues = frappe.get_all('Issue', {
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 7f42cd9..dd64e05 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
@@ -1,22 +1,30 @@
-# Copyright (c) 2019, Frappe and Contributors
+# Copyright (c) 2021, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
import frappe
+from erpnext.controllers.status_updater import OverAllowanceError
+
+
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')
def update_from_return_docs(doctype):
- for return_doc in frappe.get_all(doctype, filters={'is_return' : 1, 'docstatus' : 1}):
+ 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)
- return_doc.update_prevdoc_status()
+ try:
+ return_doc.update_prevdoc_status()
+ except OverAllowanceError:
+ frappe.db.rollback()
+ continue
+
return_against = frappe.get_doc(doctype, return_doc.return_against)
return_against.update_billing_status()
+ 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`
@@ -24,4 +32,4 @@
where docstatus = 1 """)
for doctype in ('Purchase Receipt', 'Delivery Note'):
- update_from_return_docs(doctype)
\ No newline at end of file
+ update_from_return_docs(doctype)
diff --git a/erpnext/patches/v13_0/update_shipment_status.py b/erpnext/patches/v13_0/update_shipment_status.py
index c425599..f2d7d1d 100644
--- a/erpnext/patches/v13_0/update_shipment_status.py
+++ b/erpnext/patches/v13_0/update_shipment_status.py
@@ -1,5 +1,6 @@
import frappe
+
def execute():
frappe.reload_doc("stock", "doctype", "shipment")
diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py
index c156ba9..bcfbf8b 100644
--- a/erpnext/patches/v13_0/update_sla_enhancements.py
+++ b/erpnext/patches/v13_0/update_sla_enhancements.py
@@ -5,6 +5,7 @@
import frappe
+
def execute():
# add holiday list and employee group fields in SLA
# change response and resolution time in priorities child table
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 0f521cb..bcdf72e 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
@@ -5,6 +5,7 @@
import frappe
+
def execute():
frappe.reload_doc('hr', 'doctype', 'shift_assignment')
if frappe.db.has_column('Shift Assignment', 'date'):
diff --git a/erpnext/patches/v13_0/update_subscription.py b/erpnext/patches/v13_0/update_subscription.py
index 871ebf1..e0ea78f 100644
--- a/erpnext/patches/v13_0/update_subscription.py
+++ b/erpnext/patches/v13_0/update_subscription.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from six import iteritems
+
def execute():
frappe.reload_doc('accounts', 'doctype', 'subscription')
@@ -38,4 +40,4 @@
UPDATE `tabSubscription Plan`
SET price_determination = %s
WHERE price_determination = %s
- """, (value, key))
\ No newline at end of file
+ """, (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 28e650e..e21fe57 100644
--- a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
+++ b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
@@ -1,9 +1,10 @@
import frappe
+
def execute():
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')
\ No newline at end of file
+ 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 3d14958..436d2e6 100644
--- a/erpnext/patches/v13_0/update_tds_check_field.py
+++ b/erpnext/patches/v13_0/update_tds_check_field.py
@@ -1,9 +1,10 @@
import frappe
+
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("""
UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0
WHERE round_off_tax_amount IS NULL
- """)
\ No newline at end of file
+ """)
diff --git a/erpnext/patches/v13_0/update_timesheet_changes.py b/erpnext/patches/v13_0/update_timesheet_changes.py
index 93b7f8e..d993d54 100644
--- a/erpnext/patches/v13_0/update_timesheet_changes.py
+++ b/erpnext/patches/v13_0/update_timesheet_changes.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc("projects", "doctype", "timesheet")
frappe.reload_doc("projects", "doctype", "timesheet_detail")
@@ -22,4 +24,4 @@
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))
\ No newline at end of file
+ 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
new file mode 100644
index 0000000..69bfaaa
--- /dev/null
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -0,0 +1,11 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc('custom', 'doctype', 'custom_field')
+ 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', '')
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 340bf49..c760a6a 100644
--- a/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
+++ b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
@@ -5,6 +5,7 @@
from frappe import _
from frappe.model.utils.rename_field import rename_field
+
def execute():
frappe.reload_doc('Accounts', 'doctype', 'Salary Component Account')
@@ -96,8 +97,8 @@
# 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',
+ '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:
diff --git a/erpnext/patches/v13_0/validate_options_for_data_field.py b/erpnext/patches/v13_0/validate_options_for_data_field.py
new file mode 100644
index 0000000..41a38fe
--- /dev/null
+++ b/erpnext/patches/v13_0/validate_options_for_data_field.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.model import data_field_options
+
+
+def execute():
+
+ for field in frappe.get_all('Custom Field',
+ fields = ['name'],
+ filters = {
+ 'fieldtype': 'Data',
+ 'options': ['!=', None]
+ }):
+
+ if field not in data_field_options:
+ frappe.db.sql("""
+ UPDATE
+ `tabCustom Field`
+ SET
+ options=NULL
+ WHERE
+ name=%s
+ """, (field))
diff --git a/erpnext/healthcare/__init__.py b/erpnext/patches/v14_0/__init__.py
similarity index 100%
rename from erpnext/healthcare/__init__.py
rename to erpnext/patches/v14_0/__init__.py
diff --git a/erpnext/patches/v14_0/delete_einvoicing_doctypes.py b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
new file mode 100644
index 0000000..a3a8149
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
@@ -0,0 +1,10 @@
+import frappe
+
+
+def execute():
+ frappe.delete_doc('DocType', 'E Invoice Settings', ignore_missing=True)
+ frappe.delete_doc('DocType', 'E Invoice User', ignore_missing=True)
+ frappe.delete_doc('Report', 'E-Invoice Summary', ignore_missing=True)
+ frappe.delete_doc('Print Format', 'GST E-Invoice', ignore_missing=True)
+ frappe.delete_doc('Custom Field', 'Sales Invoice-eway_bill_cancelled', ignore_missing=True)
+ frappe.delete_doc('Custom Field', 'Sales Invoice-irn_cancelled', ignore_missing=True)
diff --git a/erpnext/patches/v14_0/delete_shopify_doctypes.py b/erpnext/patches/v14_0/delete_shopify_doctypes.py
new file mode 100644
index 0000000..96bee39
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_shopify_doctypes.py
@@ -0,0 +1,6 @@
+import frappe
+
+
+def execute():
+ frappe.delete_doc("DocType", "Shopify Settings", ignore_missing=True)
+ frappe.delete_doc("DocType", "Shopify Log", ignore_missing=True)
diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py
new file mode 100644
index 0000000..223be7f
--- /dev/null
+++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py
@@ -0,0 +1,36 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.utils import flt
+
+import erpnext
+from erpnext.setup.utils import get_exchange_rate
+
+
+def execute():
+ frappe.reload_doc('crm', 'doctype', 'opportunity')
+ frappe.reload_doc('crm', 'doctype', 'opportunity_item')
+
+ 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)
+
+ # base total and total will be 0 only since item table did not have amount field earlier
+ if opportunity.currency != company_currency:
+ conversion_rate = get_exchange_rate(opportunity.currency, company_currency)
+ base_opportunity_amount = flt(conversion_rate) * flt(opportunity.opportunity_amount)
+ grand_total = flt(opportunity.opportunity_amount)
+ base_grand_total = flt(conversion_rate) * flt(opportunity.opportunity_amount)
+ else:
+ conversion_rate = 1
+ base_opportunity_amount = grand_total = base_grand_total = flt(opportunity.opportunity_amount)
+
+ frappe.db.set_value('Opportunity', opportunity.name, {
+ 'conversion_rate': conversion_rate,
+ 'base_opportunity_amount': base_opportunity_amount,
+ 'grand_total': grand_total,
+ 'base_grand_total': base_grand_total
+ }, update_modified=False)
diff --git a/erpnext/patches/v4_2/repost_reserved_qty.py b/erpnext/patches/v4_2/repost_reserved_qty.py
index 36117aa..4fa3b46 100644
--- a/erpnext/patches/v4_2/repost_reserved_qty.py
+++ b/erpnext/patches/v4_2/repost_reserved_qty.py
@@ -2,8 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
+
+from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
+
def execute():
for doctype in ("Sales Order Item", "Bin"):
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 7bb49e6..9330a44 100644
--- a/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
+++ b/erpnext/patches/v4_2/update_requested_and_ordered_qty.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
- from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty, get_ordered_qty
+ 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
@@ -20,5 +22,5 @@
})
if count % 200 == 0:
frappe.db.commit()
- except:
+ 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 2045358..82b5b1c 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
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def execute():
name = frappe.db.sql(""" select name from `tabPatch Log` \
where \
diff --git a/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py b/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py
index ccb2e0e..ecfdc41 100644
--- a/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py
+++ b/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute():
frappe.reload_doc('core', 'doctype', 'has_role')
company = frappe.get_all('Company', filters = {'country': 'India'})
@@ -15,4 +17,4 @@
where
parenttype = 'Report' and parent in('GST Sales Register',
'GST Purchase Register', 'GST Itemised Sales Register',
- 'GST Itemised Purchase Register', 'Eway Bill')""")
\ No newline at end of file
+ 'GST Itemised Purchase Register', 'Eway Bill')""")
diff --git a/erpnext/patches/v8_1/setup_gst_india.py b/erpnext/patches/v8_1/setup_gst_india.py
index e8b017d..140b226 100644
--- a/erpnext/patches/v8_1/setup_gst_india.py
+++ b/erpnext/patches/v8_1/setup_gst_india.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
import frappe
from frappe.email import sendmail_to_system_managers
+
def execute():
frappe.reload_doc('stock', 'doctype', 'item')
frappe.reload_doc("stock", "doctype", "customs_tariff_number")
@@ -50,4 +52,4 @@
try:
sendmail_to_system_managers("[Important] ERPNext GST updates", message)
except Exception as e:
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py
index eb24a90..65ec14e 100644
--- a/erpnext/patches/v8_7/sync_india_custom_fields.py
+++ b/erpnext/patches/v8_7/sync_india_custom_fields.py
@@ -1,6 +1,9 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.regional.india.setup import make_custom_fields
+
+from erpnext.regional.india.setup import make_custom_fields
+
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index b978cbe..7c0a8ea 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -3,20 +3,22 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _, bold
-from frappe.utils import getdate, date_diff, comma_and, formatdate
+from frappe.model.document import Document
+from frappe.utils import comma_and, date_diff, formatdate, getdate
+
from erpnext.hr.utils import validate_active_employee
+
class AdditionalSalary(Document):
def on_submit(self):
- if self.ref_doctype == "Employee Advance" and self.ref_docname:
- frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
-
+ self.update_return_amount_in_employee_advance()
self.update_employee_referral()
def on_cancel(self):
+ self.update_return_amount_in_employee_advance()
self.update_employee_referral(cancel=True)
def validate(self):
@@ -95,6 +97,17 @@
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:
+ return_amount = frappe.db.get_value("Employee Advance", self.ref_docname, "return_amount")
+
+ if self.docstatus == 2:
+ return_amount -= self.amount
+ else:
+ return_amount += self.amount
+
+ frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount)
+
def update_employee_referral(self, cancel=False):
if self.ref_doctype == "Employee Referral":
status = "Unpaid" if cancel else "Paid"
@@ -112,11 +125,11 @@
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
return amount_per_day * no_of_days
-@frappe.whitelist()
def get_additional_salaries(employee, start_date, end_date, component_type):
additional_salary_list = frappe.db.sql("""
- select name, salary_component as component, type, amount, overwrite_salary_structure_amount as overwrite,
- deduct_full_tax_on_selected_payroll_date, is_recurring
+ select name, salary_component as component, type, amount,
+ overwrite_salary_structure_amount as overwrite,
+ deduct_full_tax_on_selected_payroll_date
from `tabAdditional Salary`
where employee=%(employee)s
and docstatus = 1
diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.js b/erpnext/payroll/doctype/additional_salary/test_additional_salary.js
deleted file mode 100644
index c18e187..0000000
--- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Additional Salary", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Additional Salary
- () => frappe.tests.make('Additional Salary', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
index 4d47f25..2ad4afe 100644
--- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
@@ -2,12 +2,19 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
import unittest
-import frappe, erpnext
-from frappe.utils import nowdate, add_days
+
+import frappe
+from frappe.utils import add_days, nowdate
+
+import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
-from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+ make_employee_salary_slip,
+ setup_test,
+)
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
@@ -27,7 +34,7 @@
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)
add_sal = get_additional_salary(emp_id)
-
+
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":
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 5ebe514..9983f01 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
@@ -3,13 +3,26 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
from frappe.model.document import Document
-from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
-from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
-from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount, validate_active_employee
+from frappe.utils import add_days, cint, cstr, date_diff, getdate, rounded
+
+from erpnext.hr.utils import (
+ get_holiday_dates_for_employee,
+ get_previous_claimed_amount,
+ get_sal_slip_total_benefit_given,
+ validate_active_employee,
+)
+from erpnext.payroll.doctype.payroll_period.payroll_period import (
+ get_payroll_period_days,
+ get_period_factor,
+)
+from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
+ get_assigned_salary_structure,
+)
+
class EmployeeBenefitApplication(Document):
def validate(self):
@@ -139,7 +152,7 @@
# Then the sum multiply with the no of lwp in that period
# Include that amount to the prev_sal_slip_flexi_total to get the actual
if have_depends_on_payment_days and per_day_amount_total > 0:
- holidays = get_holidays_for_employee(employee, payroll_period_obj.start_date, on_date)
+ holidays = get_holiday_dates_for_employee(employee, payroll_period_obj.start_date, on_date)
working_days = date_diff(on_date, payroll_period_obj.start_date) + 1
leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days)
leave_days_amount = leave_days * per_day_amount_total
@@ -253,4 +266,4 @@
order by name
""", salary_structure, earning_component)
- return amount if amount else 0
\ No newline at end of file
+ return amount if amount else 0
diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js
deleted file mode 100644
index b355e1c..0000000
--- a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Benefit Application", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Benefit Application
- () => frappe.tests.make('Employee Benefit Application', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 34e1a8f..ea05e0e 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
@@ -2,7 +2,9 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
import unittest
+
class TestEmployeeBenefitApplication(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py
index 65405fe..ddcbaa2 100644
--- a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py
+++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeBenefitApplicationDetail(Document):
pass
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 c6713f3..7898e04 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -3,14 +3,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
from frappe.model.document import Document
-from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
+from frappe.utils import flt
+
from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee
+from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import (
+ get_max_benefits,
+)
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period
-from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
+from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
+ get_assigned_salary_structure,
+)
+
class EmployeeBenefitClaim(Document):
def validate(self):
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js
deleted file mode 100644
index 3c808c0..0000000
--- a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Benefit Claim", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Benefit Claim
- () => frappe.tests.make('Employee Benefit Claim', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py
index aff73e5..f3f2229 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py
+++ b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py
@@ -2,7 +2,9 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
import unittest
+
class TestEmployeeBenefitClaim(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
index 6b918ba..74d0736 100644
--- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
from erpnext.hr.utils import validate_active_employee
+
class EmployeeIncentive(Document):
def validate(self):
validate_active_employee(self.employee)
diff --git a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js
deleted file mode 100644
index 10bc037..0000000
--- a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Incentive", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Incentive
- () => frappe.tests.make('Employee Incentive', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
index f7597ad..3c95fa8 100644
--- a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
+++ b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeIncentive(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
index ab63c0d..73a0321 100644
--- a/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
+++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeOtherIncome(Document):
pass
diff --git a/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py
index 2eeca7a..5b735b3 100644
--- a/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py
+++ b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestEmployeeOtherIncome(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py
index 4f705db..f88f551 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class EmployeeTaxExemptionCategory(Document):
pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js
deleted file mode 100644
index e0e43c3..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Tax Exemption Category
- () => frappe.tests.make('Employee Tax Exemption Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py
index 669fb71..e6bc3ab 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeTaxExemptionCategory(unittest.TestCase):
pass
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 e11d60a..099ab0d 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
@@ -3,13 +3,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
-from frappe import _
-from frappe.utils import flt
from frappe.model.mapper import get_mapped_doc
-from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
- calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
+from frappe.utils import flt
+
+from erpnext.hr.utils import (
+ calculate_annual_eligible_hra_exemption,
+ get_total_exemption_amount,
+ validate_active_employee,
+ validate_duplicate_exemption_for_payroll_period,
+ validate_tax_declaration,
+)
+
class EmployeeTaxExemptionDeclaration(Document):
def validate(self):
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js
deleted file mode 100644
index 274a3a3..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Declaration", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Tax Exemption Declaration
- () => frappe.tests.make('Employee Tax Exemption Declaration', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 311f352..b7154a4 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
@@ -3,11 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
import unittest
+
+import frappe
+
+import erpnext
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.utils import DuplicateDeclarationError
+
class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
def setUp(self):
make_employee("employee@taxexepmtion.com")
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py
index bff747f..2923e57 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeTaxExemptionDeclarationCategory(Document):
pass
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 8131ae0..f35fd3c 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
@@ -3,12 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
-from frappe import _
from frappe.utils import flt
-from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
- calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period
+
+from erpnext.hr.utils import (
+ calculate_hra_exemption_for_period,
+ get_total_exemption_amount,
+ validate_active_employee,
+ validate_duplicate_exemption_for_payroll_period,
+ validate_tax_declaration,
+)
+
class EmployeeTaxExemptionProofSubmission(Document):
def validate(self):
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js
deleted file mode 100644
index cec7508..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Proof Submission", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Tax Exemption Proof Submission
- () => frappe.tests.make('Employee Tax Exemption Proof Submission', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
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 cb9ed5f..aafc0a1 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
@@ -3,9 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period
+
+import frappe
+
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
+ create_exemption_category,
+ create_payroll_period,
+)
+
class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase):
def setup(self):
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py
index 0244ae6..e0a11ae 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class EmployeeTaxExemptionProofSubmissionDetail(Document):
pass
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 a8dd7e4..5948ef2 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
@@ -3,14 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
from frappe.model.document import Document
+from frappe.utils import flt
+
class EmployeeTaxExemptionSubCategory(Document):
def validate(self):
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))
\ No newline at end of file
+ .format(category_max_amount, self.exemption_category))
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js
deleted file mode 100644
index 8a1a6d1..0000000
--- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Employee Tax Exemption Sub Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Employee Tax Exemption Sub Category
- () => frappe.tests.make('Employee Tax Exemption Sub Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py
index 5d705567..0086086 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestEmployeeTaxExemptionSubCategory(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.js b/erpnext/payroll/doctype/gratuity/gratuity.js
index 565d2c4..d4f7c9c 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.js
+++ b/erpnext/payroll/doctype/gratuity/gratuity.js
@@ -3,13 +3,6 @@
frappe.ui.form.on('Gratuity', {
setup: function (frm) {
- frm.set_query('salary_component', function () {
- return {
- filters: {
- type: "Earning"
- }
- };
- });
frm.set_query("expense_account", function () {
return {
filters: {
@@ -31,7 +24,7 @@
});
},
refresh: function (frm) {
- if (frm.doc.docstatus === 1 && frm.doc.pay_via_salary_slip === 0 && frm.doc.status === "Unpaid") {
+ if (frm.doc.docstatus == 1 && frm.doc.status == "Unpaid") {
frm.add_custom_button(__("Create Payment Entry"), function () {
return frappe.call({
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry',
@@ -69,4 +62,4 @@
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.json b/erpnext/payroll/doctype/gratuity/gratuity.json
index 5cffd7e..48a9ce4 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.json
+++ b/erpnext/payroll/doctype/gratuity/gratuity.json
@@ -16,9 +16,6 @@
"company",
"gratuity_rule",
"section_break_5",
- "pay_via_salary_slip",
- "payroll_date",
- "salary_component",
"payable_account",
"expense_account",
"mode_of_payment",
@@ -50,26 +47,12 @@
"reqd": 1
},
{
- "default": "1",
- "fieldname": "pay_via_salary_slip",
- "fieldtype": "Check",
- "label": "Pay via Salary Slip"
- },
- {
"fieldname": "posting_date",
"fieldtype": "Date",
"label": "Posting date",
"reqd": 1
},
{
- "depends_on": "eval: doc.pay_via_salary_slip == 1",
- "fieldname": "salary_component",
- "fieldtype": "Link",
- "label": "Salary Component",
- "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 1",
- "options": "Salary Component"
- },
- {
"default": "0",
"fieldname": "current_work_experience",
"fieldtype": "Int",
@@ -95,20 +78,18 @@
"reqd": 1
},
{
- "depends_on": "eval: doc.pay_via_salary_slip == 0",
"fieldname": "expense_account",
"fieldtype": "Link",
"label": "Expense Account",
- "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
- "options": "Account"
+ "options": "Account",
+ "reqd": 1
},
{
- "depends_on": "eval: doc.pay_via_salary_slip == 0",
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"label": "Mode of Payment",
- "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
- "options": "Mode of Payment"
+ "options": "Mode of Payment",
+ "reqd": 1
},
{
"fieldname": "gratuity_rule",
@@ -162,13 +143,6 @@
"fieldtype": "Column Break"
},
{
- "depends_on": "eval: doc.pay_via_salary_slip == 1",
- "fieldname": "payroll_date",
- "fieldtype": "Date",
- "label": "Payroll Date",
- "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 1"
- },
- {
"default": "0",
"depends_on": "eval:doc.pay_via_salary_slip == 0",
"fieldname": "paid_amount",
@@ -177,26 +151,23 @@
"read_only": 1
},
{
- "depends_on": "eval: doc.pay_via_salary_slip == 0",
"fieldname": "payable_account",
"fieldtype": "Link",
"label": "Payable Account",
- "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
- "options": "Account"
+ "options": "Account",
+ "reqd": 1
},
{
- "depends_on": "eval: doc.pay_via_salary_slip == 0",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
- "mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
"options": "Cost Center"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-11-02 18:21:11.971488",
+ "modified": "2021-07-02 15:05:57.396398",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Gratuity",
diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py
index 1acd6e3..cc28dc4 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity.py
@@ -3,12 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from math import floor
+
import frappe
from frappe import _, bold
from frappe.utils import flt, get_datetime, get_link_to_form
+
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.controllers.accounts_controller import AccountsController
-from math import floor
+
class Gratuity(AccountsController):
def validate(self):
@@ -19,10 +23,7 @@
self.status = "Unpaid"
def on_submit(self):
- if self.pay_via_salary_slip:
- self.create_additional_salary()
- else:
- self.create_gl_entries()
+ self.create_gl_entries()
def on_cancel(self):
self.ignore_linked_doctypes = ['GL Entry']
@@ -65,19 +66,6 @@
return gl_entry
- def create_additional_salary(self):
- if self.pay_via_salary_slip:
- 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
- additional_salary.amount = self.amount
- additional_salary.payroll_date = self.payroll_date
- additional_salary.company = self.company
- additional_salary.ref_doctype = self.doctype
- additional_salary.ref_docname = self.name
- additional_salary.submit()
-
def set_total_advance_paid(self):
paid_amount = frappe.db.sql("""
select ifnull(sum(debit_in_account_currency), 0) as paid_amount
@@ -242,8 +230,11 @@
order_by = "from_date desc")[0].salary_structure
def get_last_salary_slip(employee):
- return frappe.get_list("Salary Slip", filters = {
+ salary_slips = frappe.get_list("Salary Slip", filters = {
"employee": employee, 'docstatus': 1
},
- order_by = "start_date desc")[0].name
-
+ order_by = "start_date desc"
+ )
+ if not salary_slips:
+ return
+ return salary_slips[0].name
diff --git a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
index 5b2489f..1599fc2 100644
--- a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
+++ b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'reference_name',
@@ -11,10 +13,6 @@
{
'label': _('Payment'),
'items': ['Payment Entry']
- },
- {
- 'label': _('Additional Salary'),
- 'items': ['Additional Salary']
}
]
- }
\ 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 7daea2d..1403e9b 100644
--- a/erpnext/payroll/doctype/gratuity/test_gratuity.py
+++ b/erpnext/payroll/doctype/gratuity/test_gratuity.py
@@ -3,15 +3,20 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+from frappe.utils import add_days, flt, get_datetime, getdate
+
from erpnext.hr.doctype.employee.test_employee import make_employee
-from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, make_earning_salary_component, \
- make_deduction_salary_component
-from erpnext.payroll.doctype.gratuity.gratuity import get_last_salary_slip
-from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
-from frappe.utils import getdate, add_days, get_datetime, flt
+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,
+)
+from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
test_dependencies = ["Salary Component", "Salary Slip", "Account"]
class TestGratuity(unittest.TestCase):
@@ -22,14 +27,18 @@
def setUp(self):
frappe.db.sql("DELETE FROM `tabGratuity`")
- frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
- def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creation(self):
+ def test_get_last_salary_slip_should_return_none_for_new_employee(self):
+ new_employee = make_employee("new_employee@salary.com", company='_Test Company')
+ salary_slip = get_last_salary_slip(new_employee)
+ assert salary_slip is None
+
+ def test_check_gratuity_amount_based_on_current_slab(self):
employee, sal_slip = create_employee_and_get_last_salary_slip()
rule = get_gratuity_rule("Rule Under Unlimited Contract on termination (UAE)")
- gratuity = create_gratuity(pay_via_salary_slip = 1, employee=employee, rule=rule.name)
+ gratuity = create_gratuity(employee=employee, rule=rule.name)
#work experience calculation
date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date'])
@@ -57,9 +66,6 @@
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()
rule = get_gratuity_rule("Rule Under Limited Contract (UAE)")
@@ -137,14 +143,9 @@
gratuity.employee = args.employee
gratuity.posting_date = getdate()
gratuity.gratuity_rule = args.rule or "Rule Under Limited Contract (UAE)"
- gratuity.pay_via_salary_slip = args.pay_via_salary_slip or 0
- if gratuity.pay_via_salary_slip:
- gratuity.payroll_date = getdate()
- gratuity.salary_component = "Performance Bonus"
- else:
- 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.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.save()
gratuity.submit()
diff --git a/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py b/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py
index 23e4340..d76b26d 100644
--- a/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py
+++ b/erpnext/payroll/doctype/gratuity_applicable_component/gratuity_applicable_component.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class GratuityApplicableComponent(Document):
pass
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
index ee6c5df..014a121 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
@@ -37,4 +37,4 @@
frappe.throw(__("To(Year) year can not be less than From(year) "));
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
index 29a6ebe..95d2929 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
@@ -3,16 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
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(_("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."))
diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
index 0d70163..60dcfa4 100644
--- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
+++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'gratuity_rule',
@@ -10,4 +12,4 @@
'items': ['Gratuity']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py b/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
index 1f5dc4e..c81d7b7 100644
--- a/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
+++ b/erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestGratuityRule(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py b/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py
index fa468e7..dcd7e46 100644
--- a/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py
+++ b/erpnext/payroll/doctype/gratuity_rule_slab/gratuity_rule_slab.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class GratuityRuleSlab(Document):
pass
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 81e3647..f778fd9 100644
--- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
@@ -3,9 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from frappe.model.document import Document
+
#import frappe
import erpnext
-from frappe.model.document import Document
+
class IncomeTaxSlab(Document):
def validate(self):
diff --git a/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py
index deaaf65..d762990 100644
--- a/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py
+++ b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestIncomeTaxSlab(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py
index b4098ec..3314677 100644
--- a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py
+++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class IncomeTaxSlabOtherCharges(Document):
pass
diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
index aeb11fd..074d223 100644
--- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
+++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class PayrollEmployeeDetail(Document):
pass
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 13cc423..f1557c7 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -3,15 +3,30 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.model.document import Document
+
+import frappe
from dateutil.relativedelta import relativedelta
-from frappe.utils import cint, flt, add_days, getdate, add_to_date, DATE_FORMAT, date_diff, comma_and
from frappe import _
+from frappe.desk.reportview import get_filters_cond, get_match_cond
+from frappe.model.document import Document
+from frappe.utils import (
+ DATE_FORMAT,
+ add_days,
+ add_to_date,
+ cint,
+ comma_and,
+ date_diff,
+ flt,
+ getdate,
+)
+
+import erpnext
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
from erpnext.accounts.utils import get_fiscal_year
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
-from frappe.desk.reportview import get_match_cond, get_filters_cond
-from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+
class PayrollEntry(Document):
def onload(self):
@@ -529,7 +544,8 @@
def get_month_details(year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
- import calendar, datetime
+ import calendar
+ import datetime
diff_mnt = cint(month)-cint(ysd.month)
if diff_mnt<0:
diff_mnt = 12-int(ysd.month)+cint(month)
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
index 7af507d..16e44d0 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -13,4 +13,4 @@
'items': ['Salary Slip', 'Journal Entry']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index b80b320..dd0e127 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -1,19 +1,37 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
-import erpnext
+
import frappe
from dateutil.relativedelta import relativedelta
-from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate
from frappe.utils import add_months
-from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date
+
+import erpnext
+from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate
from erpnext.hr.doctype.employee.test_employee import make_employee
-from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
- make_earning_salary_component, make_deduction_salary_component, create_account, make_employee_salary_slip
-from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment
-from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry, create_loan_type, create_loan_accounts
-from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
+from erpnext.loan_management.doctype.loan.test_loan import (
+ create_loan,
+ create_loan_accounts,
+ create_loan_type,
+ make_loan_disbursement_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.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,
+)
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+ create_salary_structure_assignment,
+ make_salary_structure,
+)
test_dependencies = ['Holiday List']
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py
index ef3a6cc..0de8d63 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt, add_months
from frappe.model.document import Document
-from erpnext.hr.utils import get_holidays_for_employee
+from frappe.utils import add_months, cint, date_diff, flt, formatdate, getdate, month_diff
+
+from erpnext.hr.utils import get_holiday_dates_for_employee
+
class PayrollPeriod(Document):
def validate(self):
@@ -65,7 +68,7 @@
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_holidays_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2]))
+ 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
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
index 4e9c7c9..4105d8e 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Employee Tax Exemption Proof Submission', 'Employee Tax Exemption Declaration']
},
],
- }
\ No newline at end of file
+ }
diff --git a/erpnext/payroll/doctype/payroll_period/test_payroll_period.js b/erpnext/payroll/doctype/payroll_period/test_payroll_period.js
deleted file mode 100644
index 8c4ded9..0000000
--- a/erpnext/payroll/doctype/payroll_period/test_payroll_period.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Payroll Period", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Payroll Period
- () => frappe.tests.make('Payroll Period', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/payroll_period/test_payroll_period.py b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
index d06dc73..e93c0e5 100644
--- a/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
+++ b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestPayrollPeriod(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
index a3ee269..fa6835d 100644
--- a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
+++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class PayrollPeriodDate(Document):
pass
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
index 459b7ea..b85d554 100644
--- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.model.document import Document
from frappe.utils import cint
-from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-from frappe import _
+
class PayrollSettings(Document):
def validate(self):
diff --git a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
index 314866e..30a6a33 100644
--- a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
+++ b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestPayrollSettings(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
index 055bea7..7e731e7 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -3,11 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
from frappe.utils import getdate
+
from erpnext.hr.utils import validate_active_employee
+
+
class RetentionBonus(Document):
def validate(self):
validate_active_employee(self.employee)
diff --git a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js
deleted file mode 100644
index a4b95d3..0000000
--- a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Retention Bonus", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Retention Bonus
- () => frappe.tests.make('Retention Bonus', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
index eef4f14..a380d9f 100644
--- a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestRetentionBonus(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js
index e9e6f81..dbf7514 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.js
+++ b/erpnext/payroll/doctype/salary_component/salary_component.js
@@ -4,18 +4,11 @@
frappe.ui.form.on('Salary Component', {
setup: function(frm) {
frm.set_query("account", "accounts", function(doc, cdt, cdn) {
- let d = frappe.get_doc(cdt, cdn);
-
- let root_type = "Liability";
- if (frm.doc.type == "Deduction") {
- root_type = "Expense";
- }
-
+ var d = locals[cdt][cdn];
return {
filters: {
"is_group": 0,
- "company": d.company,
- "root_type": root_type
+ "company": d.company
}
};
});
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py
index 7c92631..761d443 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.py
+++ b/erpnext/payroll/doctype/salary_component/salary_component.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
from frappe.model.naming import append_number_if_name_exists
+
class SalaryComponent(Document):
def validate(self):
self.validate_abbr()
diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.js b/erpnext/payroll/doctype/salary_component/test_salary_component.js
deleted file mode 100644
index c47d32d..0000000
--- a/erpnext/payroll/doctype/salary_component/test_salary_component.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Salary Component", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Salary Component
- () => frappe.tests.make('Salary Component', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.py b/erpnext/payroll/doctype/salary_component/test_salary_component.py
index 4f7db0c..939fa4a 100644
--- a/erpnext/payroll/doctype/salary_component/test_salary_component.py
+++ b/erpnext/payroll/doctype/salary_component/test_salary_component.py
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
# test_records = frappe.get_test_records('Salary Component')
class TestSalaryComponent(unittest.TestCase):
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json
index 97608d7..393f647 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.json
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json
@@ -12,7 +12,6 @@
"year_to_date",
"section_break_5",
"additional_salary",
- "is_recurring_additional_salary",
"statistical_component",
"depends_on_payment_days",
"exempted_from_income_tax",
@@ -236,19 +235,11 @@
"label": "Year To Date",
"options": "currency",
"read_only": 1
- },
- {
- "default": "0",
- "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='earnings' && doc.additional_salary",
- "fieldname": "is_recurring_additional_salary",
- "fieldtype": "Check",
- "label": "Is Recurring Additional Salary",
- "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2021-03-14 13:39:15.847158",
+ "modified": "2021-01-14 13:39:15.847158",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Detail",
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.py b/erpnext/payroll/doctype/salary_detail/salary_detail.py
index 0b18754..50d1958 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.py
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SalaryDetail(Document):
pass
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
index 5258f3a..3ef9762 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -135,15 +135,15 @@
change_form_labels: function(frm, company_currency) {
frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction",
- "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"],
+ "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "gross_base_year_to_date"],
company_currency);
- frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date"],
+ frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date", "gross_year_to_date"],
frm.doc.currency);
// toggle fields
frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction",
- "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"],
+ "base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "base_gross_year_to_date"],
frm.doc.currency != company_currency);
},
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
index 42a0f29..7a80e69 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.json
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -56,6 +56,8 @@
"totals",
"gross_pay",
"base_gross_pay",
+ "gross_year_to_date",
+ "base_gross_year_to_date",
"column_break_25",
"total_deduction",
"base_total_deduction",
@@ -327,7 +329,7 @@
{
"fieldname": "earning_deduction",
"fieldtype": "Section Break",
- "label": "Earning & Deduction",
+ "label": "Earnings & Deductions",
"oldfieldtype": "Section Break"
},
{
@@ -378,7 +380,7 @@
"depends_on": "total_loan_repayment",
"fieldname": "loan_repayment",
"fieldtype": "Section Break",
- "label": "Loan repayment"
+ "label": "Loan Repayment"
},
{
"fieldname": "loans",
@@ -423,7 +425,7 @@
{
"fieldname": "net_pay_info",
"fieldtype": "Section Break",
- "label": "net pay info"
+ "label": "Net Pay Info"
},
{
"fieldname": "net_pay",
@@ -625,13 +627,27 @@
"label": "Leave Details",
"options": "Salary Slip Leave",
"read_only": 1
+ },
+ {
+ "fieldname": "gross_year_to_date",
+ "fieldtype": "Currency",
+ "label": "Gross Year To Date",
+ "options": "currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "base_gross_year_to_date",
+ "fieldtype": "Currency",
+ "label": "Gross Year To Date(Company Currency)",
+ "options": "Company:company:default_currency",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 9,
"is_submittable": 1,
"links": [],
- "modified": "2021-03-31 22:44:09.772331",
+ "modified": "2021-10-08 11:47:47.098248",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",
@@ -672,4 +688,4 @@
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "employee_name"
-}
\ No newline at end of file
+}
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index f0ca64f..d113e7e 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -2,26 +2,51 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-import datetime, math
-from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day
+import datetime
+import math
+
+import frappe
+from frappe import _, msgprint
from frappe.model.naming import make_autoname
+from frappe.utils import (
+ add_days,
+ cint,
+ cstr,
+ date_diff,
+ flt,
+ formatdate,
+ get_first_day,
+ getdate,
+ money_in_words,
+ rounded,
+)
from frappe.utils.background_jobs import enqueue
-
-from frappe import msgprint, _
-from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
-from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
-from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
-from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
-from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
-from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
-from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext.hr.utils import validate_active_employee
from six import iteritems
+import erpnext
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.hr.utils import get_holiday_dates_for_employee, validate_active_employee
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+ calculate_amounts,
+ create_repayment_entry,
+)
+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,
+)
+from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import (
+ get_benefit_claim_amount,
+ get_last_payroll_period_benefits,
+)
+from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
+from erpnext.payroll.doctype.payroll_period.payroll_period import (
+ get_payroll_period,
+ get_period_factor,
+)
+from erpnext.utilities.transaction_base import TransactionBase
+
+
class SalarySlip(TransactionBase):
def __init__(self, *args, **kwargs):
super(SalarySlip, self).__init__(*args, **kwargs)
@@ -337,20 +362,7 @@
return payment_days
def get_holidays_for_employee(self, start_date, end_date):
- holiday_list = get_holiday_list_for_employee(self.employee)
- holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
- where
- parent=%(holiday_list)s
- and holiday_date >= %(start_date)s
- and holiday_date <= %(end_date)s''', {
- "holiday_list": holiday_list,
- "start_date": start_date,
- "end_date": end_date
- })
-
- holidays = [cstr(i) for i in holidays]
-
- return holidays
+ return get_holiday_dates_for_employee(self.employee, start_date, end_date)
def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days):
lwp = 0
@@ -475,7 +487,7 @@
self.calculate_component_amounts("deductions")
self.set_loan_repayment()
- self.set_component_amounts_based_on_payment_days()
+ self.set_precision_for_component_amounts()
self.set_net_pay()
def set_net_pay(self):
@@ -618,8 +630,7 @@
get_salary_component_data(additional_salary.component),
additional_salary.amount,
component_type,
- additional_salary,
- is_recurring = additional_salary.is_recurring
+ additional_salary
)
def add_tax_components(self, payroll_period):
@@ -640,7 +651,7 @@
tax_row = get_salary_component_data(d)
self.update_component_row(tax_row, tax_amount, "deductions")
- def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0):
+ def update_component_row(self, component_data, amount, component_type, additional_salary=None):
component_row = None
for d in self.get(component_type):
if d.salary_component != component_data.salary_component:
@@ -681,7 +692,6 @@
component_row.set(attr, component_data.get(attr))
if additional_salary:
- component_row.is_recurring_additional_salary = is_recurring
if additional_salary.overwrite:
component_row.additional_amount = flt(flt(amount) - flt(component_row.get("default_amount", 0)),
component_row.precision("additional_amount"))
@@ -699,6 +709,17 @@
component_row.amount = amount
+ self.update_component_amount_based_on_payment_days(component_row)
+
+ 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]
+
+ def set_precision_for_component_amounts(self):
+ for component_type in ("earnings", "deductions"):
+ for component_row in self.get(component_type):
+ component_row.amount = flt(component_row.amount, component_row.precision("amount"))
+
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}.")
@@ -719,7 +740,6 @@
# 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]
-
# 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)
@@ -857,14 +877,7 @@
return total_tax_paid
def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0):
- joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
- ["date_of_joining", "relieving_date"])
-
- 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)))
+ joining_date, relieving_date = self.get_joining_and_relieving_dates()
taxable_earnings = 0
additional_income = 0
@@ -875,20 +888,15 @@
if based_on_payment_days:
amount, additional_amount = self.get_amount_based_on_payment_days(earning, joining_date, relieving_date)
else:
- amount, additional_amount = earning.amount, earning.additional_amount
+ if earning.additional_amount:
+ amount, additional_amount = earning.amount, earning.additional_amount
+ else:
+ amount, additional_amount = earning.default_amount, earning.additional_amount
if earning.is_tax_applicable:
if additional_amount:
- if not earning.is_recurring_additional_salary:
- taxable_earnings += (amount - additional_amount)
- additional_income += additional_amount
- else:
- to_date = frappe.db.get_value("Additional Salary", earning.additional_salary, 'to_date')
- period = (getdate(to_date).month - getdate(self.start_date).month) + 1
- if period > 0:
- taxable_earnings += (amount - additional_amount) * period
- additional_income += additional_amount * period
-
+ taxable_earnings += (amount - additional_amount)
+ additional_income += additional_amount
if earning.deduct_full_tax_on_selected_payroll_date:
additional_income_with_full_tax += additional_amount
continue
@@ -1054,7 +1062,7 @@
total += amount
return total
- def set_component_amounts_based_on_payment_days(self):
+ def get_joining_and_relieving_dates(self):
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
@@ -1064,9 +1072,7 @@
if not joining_date:
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
- for component_type in ("earnings", "deductions"):
- for d in self.get(component_type):
- d.amount = flt(self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0], d.precision("amount"))
+ return joining_date, relieving_date
def set_loan_repayment(self):
self.total_loan_repayment = 0
@@ -1237,7 +1243,7 @@
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 sum'],
+ fields = ['sum(net_pay) as net_sum', 'sum(gross_pay) as gross_sum'],
filters = {'employee_name' : self.employee_name,
'start_date' : ['>=', period_start_date],
'end_date' : ['<', period_end_date],
@@ -1245,10 +1251,13 @@
'docstatus': 1
})
- year_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
+ 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
year_to_date += self.net_pay
+ gross_year_to_date += self.gross_pay
self.year_to_date = year_to_date
+ self.gross_year_to_date = gross_year_to_date
def compute_month_to_date(self):
month_to_date = 0
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
index 06a1c7d..a47eba1 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
@@ -52,4 +52,4 @@
() => frappe.click_button('Yes'),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index d730fcf..178cd5c 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -2,20 +2,35 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import unittest
-import frappe
-import erpnext
import calendar
import random
+import unittest
+
+import frappe
+from frappe.utils import (
+ add_days,
+ add_months,
+ cstr,
+ flt,
+ get_first_day,
+ get_last_day,
+ getdate,
+ nowdate,
+)
+from frappe.utils.make_random import get_random
+
+import erpnext
from erpnext.accounts.utils import get_fiscal_year
-from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day, cstr
-from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
-from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details
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
-from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \
- import create_payroll_period, create_exemption_category
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
+ create_exemption_category,
+ create_payroll_period,
+)
+from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
+
class TestSalarySlip(unittest.TestCase):
def setUp(self):
@@ -120,6 +135,59 @@
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+ 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")
+
+ # 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("""
+ 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]
+
+ 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)
+ self.assertEqual(ss.absent_days, 1)
+
+ ss.reload()
+ payment_days_based_comp_amount = 0
+ for component in ss.earnings:
+ if component.salary_component == "HRA - Payment Days":
+ payment_days_based_comp_amount = flt(component.amount, component.precision("amount"))
+ break
+
+ # check if the dependent component is calculated using the amount updated after payment days
+ actual_amount = 0
+ precision = 0
+ for component in ss.deductions:
+ if component.salary_component == "P - Employee Provident Fund":
+ precision = component.precision("amount")
+ actual_amount = flt(component.amount, precision)
+ break
+
+ 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")
+
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)
@@ -154,7 +222,9 @@
self.assertEqual(ss.gross_pay, 78000)
def test_payment_days(self):
- from erpnext.payroll.doctype.salary_structure.test_salary_structure import create_salary_structure_assignment
+ 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
@@ -231,8 +301,15 @@
self.assertTrue(email_queue)
def test_loan_repayment_salary_slip(self):
- from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts
- from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
+ from erpnext.loan_management.doctype.loan.test_loan import (
+ create_loan,
+ create_loan_accounts,
+ create_loan_type,
+ make_loan_disbursement_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.salary_structure.test_salary_structure import make_salary_structure
applicant = make_employee("test_loan_repayment_salary_slip@salary.com", company="_Test Company")
@@ -386,8 +463,7 @@
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, create_salary_structure_assignment
+ 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,
@@ -828,7 +904,8 @@
def make_holiday_list():
fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
- if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
+ holiday_list = frappe.db.exists("Holiday List", "Salary Slip Test Holiday List")
+ if not holiday_list:
holiday_list = frappe.get_doc({
"doctype": "Holiday List",
"holiday_list_name": "Salary Slip Test Holiday List",
@@ -838,3 +915,95 @@
}).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 = [
+ {
+ "salary_component": "Basic Salary - Payment Days",
+ "abbr": "P_BS",
+ "type": "Earning",
+ "formula": "base",
+ "amount_based_on_formula": 1
+ },
+ {
+ "salary_component": "HRA - Payment Days",
+ "abbr": "P_HRA",
+ "type": "Earning",
+ "depends_on_payment_days": 1,
+ "amount_based_on_formula": 1,
+ "formula": "base * 0.20"
+ }
+ ]
+
+ make_salary_component(earnings, False, company_list=["_Test Company"])
+
+ deductions = [
+ {
+ "salary_component": "P - Professional Tax",
+ "abbr": "P_PT",
+ "type": "Deduction",
+ "depends_on_payment_days": 1,
+ "amount": 200.00
+ },
+ {
+ "salary_component": "P - Employee Provident Fund",
+ "abbr": "P_EPF",
+ "type": "Deduction",
+ "exempted_from_income_tax": 1,
+ "amount_based_on_formula": 1,
+ "depends_on_payment_days": 0,
+ "formula": "(gross_pay - P_HRA) * 0.12"
+ }
+ ]
+
+ make_salary_component(deductions, False, company_list=["_Test Company"])
+
+ salary_structure = "Salary Structure with PF"
+ if frappe.db.exists("Salary Structure", salary_structure):
+ frappe.db.delete("Salary Structure", salary_structure)
+
+ details = {
+ "doctype": "Salary Structure",
+ "name": salary_structure,
+ "company": "_Test Company",
+ "payroll_frequency": "Monthly",
+ "payment_account": get_random("Account", filters={"account_currency": "INR"}),
+ "currency": "INR"
+ }
+
+ salary_structure_doc = frappe.get_doc(details)
+
+ for entry in earnings:
+ salary_structure_doc.append("earnings", entry)
+
+ for entry in deductions:
+ salary_structure_doc.append("deductions", entry)
+
+ salary_structure_doc.insert()
+ salary_structure_doc.submit()
+
+ 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
+ )
+
+ salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name})
+
+ if not salary_slip_name:
+ salary_slip = make_salary_slip(salary_structure, employee=employee.name)
+ salary_slip.employee_name = employee.employee_name
+ salary_slip.payroll_frequency = "Monthly"
+ salary_slip.posting_date = nowdate()
+ salary_slip.insert()
+ else:
+ salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
+
+ return salary_slip
diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
index 7a92bf1..fc8282b 100644
--- a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
+++ b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class SalarySlipLeave(Document):
pass
diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
index 7adb12e..79c4c6e 100644
--- a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
+++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class SalarySlipTimesheet(Document):
pass
diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
index d07a1ab..0f6cc37 100644
--- a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
+++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
@@ -44,4 +44,4 @@
<pre><code>Condition: annual_taxable_earning > 20000000</code></pre>
<pre><code>Formula: annual_taxable_earning * 0.10 </code></pre>
</li>
-</ul>
\ No newline at end of file
+</ul>
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py
index 58c445f..ef401b2 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py
@@ -2,13 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import flt, cint, cstr
+import frappe
from frappe import _
-from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
-from six import iteritems
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, cstr, flt
+
+import erpnext
+
class SalaryStructure(Document):
def validate(self):
@@ -206,4 +208,3 @@
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 547f2b8..11d9a94 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -15,4 +15,4 @@
'items': ['Employee Grade']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index 3957d83..ff4a55e 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -2,17 +2,24 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-import erpnext
-from frappe.utils.make_random import get_random
-from frappe.utils import nowdate, add_years, get_first_day, date_diff
-from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
-from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\
- make_deduction_salary_component, make_employee_salary_slip, create_tax_slab
-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
+import frappe
+from frappe.utils import add_years, date_diff, get_first_day, nowdate
+from frappe.utils.make_random import get_random
+
+import erpnext
+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_tax_slab,
+ make_deduction_salary_component,
+ make_earning_salary_component,
+ make_employee_salary_slip,
+)
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
test_dependencies = ["Fiscal Year"]
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 a0c3013..385cf36 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate
from frappe.model.document import Document
+from frappe.utils import getdate
+
class DuplicateAssignment(frappe.ValidationError): pass
@@ -36,7 +38,7 @@
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')
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))
@@ -69,4 +71,4 @@
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))
- return employee_currency
\ No newline at end of file
+ return employee_currency
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js
deleted file mode 100644
index 2f52576..0000000
--- a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Salary Structure Assignment", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Salary Structure Assignment
- () => frappe.tests.make('Salary Structure Assignment', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py
index a9833bf..fbb894c 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestSalaryStructureAssignment(unittest.TestCase):
pass
diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
index 49c5255..c0827c4 100644
--- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
+++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class TaxableSalarySlab(Document):
pass
diff --git a/erpnext/payroll/notification/as b/erpnext/payroll/notification/as
index 7a39557..05c2c1b 100644
--- a/erpnext/payroll/notification/as
+++ b/erpnext/payroll/notification/as
@@ -1 +1 @@
-update from `tabNotification` set module='Payroll' where name = "Retention Bonus"
\ No newline at end of file
+update from `tabNotification` set module='Payroll' where name = "Retention Bonus"
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.py b/erpnext/payroll/notification/retention_bonus/retention_bonus.py
index 2334f8b..f57de91 100644
--- a/erpnext/payroll/notification/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json
index ceaf4a6..d5fee6b 100644
--- a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json
+++ b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json
@@ -13,6 +13,6 @@
"name": "Salary Slip based on Timesheet",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json
index b01239f..98a4435 100644
--- a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json
+++ b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json
@@ -16,7 +16,7 @@
"name": "Salary Slip Standard",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.js b/erpnext/payroll/report/bank_remittance/bank_remittance.js
index 6482ed3..8b75b4f 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.js
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.js
@@ -25,4 +25,3 @@
]
}
-
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
index 05a5366..d55317e 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.py
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -2,11 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import formatdate
-import itertools
from frappe import _, get_all
+
def execute(filters=None):
columns = [
{
diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js
index 4bbb7f6..6ecf2b1 100644
--- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js
@@ -4,4 +4,4 @@
frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
frappe.query_reports["Income Tax Deductions"] = erpnext.salary_slip_deductions_report_filters;
-});
\ No newline at end of file
+});
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 8a79416..296a7c2 100644
--- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
@@ -2,9 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
+import erpnext
+
+
def execute(filters=None):
data = get_data(filters)
columns = get_columns(filters) if len(data) else []
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js
index 166d982..9b82954 100644
--- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js
@@ -4,4 +4,4 @@
frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
frappe.query_reports["Salary Payments Based On Payment Mode"] = erpnext.salary_slip_deductions_report_filters;
-});
\ No newline at end of file
+});
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 a0dab63..57ea1b3 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
@@ -2,9 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions
+
+import erpnext
+from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import (
+ get_conditions,
+)
+
def execute(filters=None):
mode_of_payments = get_payment_modes()
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 d09745c..bc8fd9d 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
@@ -2,9 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
+import erpnext
+
+
def execute(filters=None):
columns = get_columns(filters)
data = get_data(filters)
diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py
index a1b1a8c..2a9dad6 100644
--- a/erpnext/payroll/report/salary_register/salary_register.py
+++ b/erpnext/payroll/report/salary_register/salary_register.py
@@ -2,9 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import flt
+
+import frappe
from frappe import _
+from frappe.utils import flt
+
+import erpnext
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py
index 4e4d477..7eeaf4b 100644
--- a/erpnext/portal/doctype/homepage/homepage.py
+++ b/erpnext/portal/doctype/homepage/homepage.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
from frappe.website.utils import delete_page_cache
+
class Homepage(Document):
def validate(self):
if not self.description:
@@ -23,4 +25,3 @@
doc.save()
self.append('products', dict(item_code=d.name,
item_name=d.item_name, description=d.description, image=d.image))
-
diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py
index e646775..bc8a828 100644
--- a/erpnext/portal/doctype/homepage/test_homepage.py
+++ b/erpnext/portal/doctype/homepage/test_homepage.py
@@ -3,11 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import set_request
from frappe.website.serve import get_response
+
class TestHomepage(unittest.TestCase):
def test_homepage_load(self):
set_request(method='GET', path='home')
diff --git a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
index 936e07d..8e8f776 100644
--- a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
+++ b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class HomepageFeaturedProduct(Document):
pass
diff --git a/erpnext/portal/doctype/homepage_section/homepage_section.py b/erpnext/portal/doctype/homepage_section/homepage_section.py
index 1ed7030..0817861 100644
--- a/erpnext/portal/doctype/homepage_section/homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/homepage_section.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
from frappe.utils import cint
+
class HomepageSection(Document):
@property
def column_value(self):
diff --git a/erpnext/portal/doctype/homepage_section/test_homepage_section.py b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
index 5bb9682..6fb7d0a 100644
--- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
@@ -3,12 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from bs4 import BeautifulSoup
from frappe.utils import set_request
from frappe.website.serve import get_response
+
class TestHomepageSection(unittest.TestCase):
def test_homepage_section_card(self):
try:
diff --git a/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py
index bd17279..b710452 100644
--- a/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py
+++ b/erpnext/portal/doctype/homepage_section_card/homepage_section_card.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class HomepageSectionCard(Document):
pass
diff --git a/erpnext/portal/doctype/products_settings/products_settings.py b/erpnext/portal/doctype/products_settings/products_settings.py
index 9a70892..d4f09b9 100644
--- a/erpnext/portal/doctype/products_settings/products_settings.py
+++ b/erpnext/portal/doctype/products_settings/products_settings.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cint
from frappe import _
from frappe.model.document import Document
+from frappe.utils import cint
+
class ProductsSettings(Document):
def validate(self):
diff --git a/erpnext/portal/doctype/products_settings/test_products_settings.js b/erpnext/portal/doctype/products_settings/test_products_settings.js
deleted file mode 100644
index b7049b3..0000000
--- a/erpnext/portal/doctype/products_settings/test_products_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Products Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Products Settings
- () => frappe.tests.make('Products Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/portal/doctype/products_settings/test_products_settings.py b/erpnext/portal/doctype/products_settings/test_products_settings.py
index d04a009..5495cc9 100644
--- a/erpnext/portal/doctype/products_settings/test_products_settings.py
+++ b/erpnext/portal/doctype/products_settings/test_products_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestProductsSettings(unittest.TestCase):
pass
diff --git a/erpnext/portal/doctype/website_attribute/website_attribute.py b/erpnext/portal/doctype/website_attribute/website_attribute.py
index b8b667a..f9ba733 100644
--- a/erpnext/portal/doctype/website_attribute/website_attribute.py
+++ b/erpnext/portal/doctype/website_attribute/website_attribute.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class WebsiteAttribute(Document):
pass
diff --git a/erpnext/portal/doctype/website_filter_field/website_filter_field.py b/erpnext/portal/doctype/website_filter_field/website_filter_field.py
index 2aa8a6f..335d457 100644
--- a/erpnext/portal/doctype/website_filter_field/website_filter_field.py
+++ b/erpnext/portal/doctype/website_filter_field/website_filter_field.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class WebsiteFilterField(Document):
pass
diff --git a/erpnext/portal/product_configurator/item_variants_cache.py b/erpnext/portal/product_configurator/item_variants_cache.py
index fc294ce..636ae8d 100644
--- a/erpnext/portal/product_configurator/item_variants_cache.py
+++ b/erpnext/portal/product_configurator/item_variants_cache.py
@@ -1,5 +1,6 @@
import frappe
+
class ItemVariantsCacheManager:
def __init__(self, item_code):
self.item_code = item_code
diff --git a/erpnext/portal/product_configurator/test_product_configurator.py b/erpnext/portal/product_configurator/test_product_configurator.py
index 8aa0734..5db74f2 100644
--- a/erpnext/portal/product_configurator/test_product_configurator.py
+++ b/erpnext/portal/product_configurator/test_product_configurator.py
@@ -1,8 +1,11 @@
from __future__ import unicode_literals
+import unittest
+
+import frappe
from bs4 import BeautifulSoup
-import frappe, unittest
from frappe.utils import get_html_for_route
+
from erpnext.portal.product_configurator.utils import get_products_for_website
test_dependencies = ["Item"]
@@ -139,4 +142,4 @@
# teardown
doc.delete()
- item_group_doc.delete()
\ No newline at end of file
+ item_group_doc.delete()
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index d60b1a2..cf623c8 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -1,8 +1,10 @@
import frappe
from frappe.utils import cint
+
from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager
-from erpnext.shopping_cart.product_info import get_product_info_for_website
from erpnext.setup.doctype.item_group.item_group import get_child_groups
+from erpnext.shopping_cart.product_info import get_product_info_for_website
+
def get_field_filter_data():
product_settings = get_product_settings()
diff --git a/erpnext/portal/utils.py b/erpnext/portal/utils.py
index d6d4469..bae8f35 100644
--- a/erpnext/portal/utils.py
+++ b/erpnext/portal/utils.py
@@ -1,9 +1,14 @@
from __future__ import unicode_literals
+
import frappe
-from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import get_shopping_cart_settings
-from erpnext.shopping_cart.cart import get_debtors_account
from frappe.utils.nestedset import get_root_of
+from erpnext.shopping_cart.cart import get_debtors_account
+from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ get_shopping_cart_settings,
+)
+
+
def set_default_role(doc, method):
'''Set customer, supplier, student, guardian based on email'''
if frappe.flags.setting_role or frappe.flags.in_migrate:
diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.js b/erpnext/projects/doctype/activity_cost/activity_cost.js
index ba10153..2d22caa 100644
--- a/erpnext/projects/doctype/activity_cost/activity_cost.js
+++ b/erpnext/projects/doctype/activity_cost/activity_cost.js
@@ -1 +1 @@
-cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
\ No newline at end of file
+cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.py b/erpnext/projects/doctype/activity_cost/activity_cost.py
index 862a707..e210324 100644
--- a/erpnext/projects/doctype/activity_cost/activity_cost.py
+++ b/erpnext/projects/doctype/activity_cost/activity_cost.py
@@ -3,17 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class DuplicationError(frappe.ValidationError): pass
class ActivityCost(Document):
def validate(self):
self.set_title()
self.check_unique()
-
+
def set_title(self):
if self.employee:
if not self.employee_name:
diff --git a/erpnext/projects/doctype/activity_cost/test_activity_cost.py b/erpnext/projects/doctype/activity_cost/test_activity_cost.py
index 67d76eb..c031f3c 100644
--- a/erpnext/projects/doctype/activity_cost/test_activity_cost.py
+++ b/erpnext/projects/doctype/activity_cost/test_activity_cost.py
@@ -3,11 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
from erpnext.projects.doctype.activity_cost.activity_cost import DuplicationError
+
class TestActivityCost(unittest.TestCase):
def test_duplication(self):
frappe.db.sql("delete from `tabActivity Cost`")
@@ -22,4 +24,4 @@
activity_cost1.insert()
activity_cost2 = frappe.copy_doc(activity_cost1)
self.assertRaises(DuplicationError, activity_cost2.insert )
- frappe.db.sql("delete from `tabActivity Cost`")
\ No newline at end of file
+ frappe.db.sql("delete from `tabActivity Cost`")
diff --git a/erpnext/projects/doctype/activity_type/activity_type.py b/erpnext/projects/doctype/activity_type/activity_type.py
index 8b610c2..4c94fe4 100644
--- a/erpnext/projects/doctype/activity_type/activity_type.py
+++ b/erpnext/projects/doctype/activity_type/activity_type.py
@@ -2,7 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class ActivityType(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.py b/erpnext/projects/doctype/activity_type/test_activity_type.py
index 3ea28df..02619af 100644
--- a/erpnext/projects/doctype/activity_type/test_activity_type.py
+++ b/erpnext/projects/doctype/activity_type/test_activity_type.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Activity Type')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Activity Type')
diff --git a/erpnext/projects/doctype/dependent_task/dependent_task.py b/erpnext/projects/doctype/dependent_task/dependent_task.py
index 90a96ac..3f62cef 100644
--- a/erpnext/projects/doctype/dependent_task/dependent_task.py
+++ b/erpnext/projects/doctype/dependent_task/dependent_task.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class DependentTask(Document):
pass
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 1e4b2b0..df970f3 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -2,19 +2,20 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _
-from six import iteritems
from email_reply_parser import EmailReplyParser
-from frappe.utils import (flt, getdate, get_url, now,
- nowtime, get_time, today, get_datetime, add_days)
-from erpnext.controllers.queries import get_filters_cond
+from frappe import _
from frappe.desk.reportview import get_match_cond
+from frappe.model.document import Document
+from frappe.utils import add_days, flt, get_datetime, get_time, get_url, nowtime, today
+
+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
-from frappe.model.document import Document
-from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
-from erpnext.controllers.employee_boarding_controller import update_employee_boarding_status
+
class Project(Document):
def get_feed(self):
@@ -143,6 +144,9 @@
if self.sales_order:
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
+ def on_trash(self):
+ frappe.db.set_value("Sales Order", {"project": self.name}, "project", "")
+
def update_percent_complete(self):
if self.percent_complete_method == "Manual":
if self.status == "Completed":
diff --git a/erpnext/projects/doctype/project/project_dashboard.html b/erpnext/projects/doctype/project/project_dashboard.html
index f5bfbb7..1f299e3 100644
--- a/erpnext/projects/doctype/project/project_dashboard.html
+++ b/erpnext/projects/doctype/project/project_dashboard.html
@@ -23,4 +23,4 @@
</span>
</div>
</div>
-{% endfor %}
\ No newline at end of file
+{% endfor %}
diff --git a/erpnext/projects/doctype/project/project_dashboard.py b/erpnext/projects/doctype/project/project_dashboard.py
index 39cf016..64fbbf5 100644
--- a/erpnext/projects/doctype/project/project_dashboard.py
+++ b/erpnext/projects/doctype/project/project_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/projects/doctype/project/test_project.js b/erpnext/projects/doctype/project/test_project.js
deleted file mode 100644
index 16494f6..0000000
--- a/erpnext/projects/doctype/project/test_project.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Project", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Project
- () => frappe.tests.make('Project', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index 70139c6..c64ac8d 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -2,12 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+import unittest
-import frappe, unittest
-from frappe.utils import getdate, nowdate, add_days
+import frappe
+from frappe.utils import add_days, getdate, nowdate
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
from erpnext.projects.doctype.task.test_task import create_task
+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_ignore = ["Sales Order"]
@@ -95,6 +98,21 @@
self.assertEqual(len(tasks), 2)
+ def test_project_linking_with_sales_order(self):
+ so = make_sales_order()
+ project = make_project_from_so(so.name)
+
+ project.save()
+ self.assertEqual(project.sales_order, so.name)
+
+ so.reload()
+ self.assertEqual(so.project, project.name)
+
+ project.delete()
+
+ so.reload()
+ self.assertFalse(so.project)
+
def get_project(name, template):
project = frappe.get_doc(dict(
diff --git a/erpnext/projects/doctype/project_template/project_template.py b/erpnext/projects/doctype/project_template/project_template.py
index aace402..493ce5b 100644
--- a/erpnext/projects/doctype/project_template/project_template.py
+++ b/erpnext/projects/doctype/project_template/project_template.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
from frappe.utils import get_link_to_form
+
class ProjectTemplate(Document):
def validate(self):
@@ -22,7 +24,7 @@
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)))
-
+
def check_dependent_task_presence(self, task):
for task_details in self.tasks:
if task_details.task == task:
diff --git a/erpnext/projects/doctype/project_template/project_template_dashboard.py b/erpnext/projects/doctype/project_template/project_template_dashboard.py
index 67f74f5..8eeaa8d 100644
--- a/erpnext/projects/doctype/project_template/project_template_dashboard.py
+++ b/erpnext/projects/doctype/project_template/project_template_dashboard.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+
def get_data():
return {
'fieldname': 'project_template',
diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py
index 95663cd..f71984f 100644
--- a/erpnext/projects/doctype/project_template/test_project_template.py
+++ b/erpnext/projects/doctype/project_template/test_project_template.py
@@ -3,10 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
from erpnext.projects.doctype.task.test_task import create_task
+
class TestProjectTemplate(unittest.TestCase):
pass
@@ -26,4 +29,4 @@
})
doc.insert()
- return frappe.get_doc('Project Template', project_template_name)
\ No newline at end of file
+ return frappe.get_doc('Project Template', project_template_name)
diff --git a/erpnext/projects/doctype/project_template_task/project_template_task.py b/erpnext/projects/doctype/project_template_task/project_template_task.py
index 57bc4f1..e086141 100644
--- a/erpnext/projects/doctype/project_template_task/project_template_task.py
+++ b/erpnext/projects/doctype/project_template_task/project_template_task.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ProjectTemplateTask(Document):
pass
diff --git a/erpnext/projects/doctype/project_type/project_type.js b/erpnext/projects/doctype/project_type/project_type.js
index a1f941f..e3dda5e 100644
--- a/erpnext/projects/doctype/project_type/project_type.js
+++ b/erpnext/projects/doctype/project_type/project_type.js
@@ -3,4 +3,4 @@
frappe.ui.form.on('Project Type', {
-});
\ No newline at end of file
+});
diff --git a/erpnext/projects/doctype/project_type/project_type.py b/erpnext/projects/doctype/project_type/project_type.py
index f46876e..1089483 100644
--- a/erpnext/projects/doctype/project_type/project_type.py
+++ b/erpnext/projects/doctype/project_type/project_type.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.model.document import Document
+
import frappe
from frappe import _
+from frappe.model.document import Document
+
class ProjectType(Document):
def on_trash(self):
if self.name == "External":
- frappe.throw(_("You cannot delete Project Type 'External'"))
\ No newline at end of file
+ frappe.throw(_("You cannot delete Project Type 'External'"))
diff --git a/erpnext/projects/doctype/project_type/test_project_type.js b/erpnext/projects/doctype/project_type/test_project_type.js
deleted file mode 100644
index c2198c4..0000000
--- a/erpnext/projects/doctype/project_type/test_project_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Project Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Project Type', [
- // insert a new Project Type
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/projects/doctype/project_type/test_project_type.py b/erpnext/projects/doctype/project_type/test_project_type.py
index ee23390..a79020f 100644
--- a/erpnext/projects/doctype/project_type/test_project_type.py
+++ b/erpnext/projects/doctype/project_type/test_project_type.py
@@ -5,5 +5,6 @@
import unittest
+
class TestProjectType(unittest.TestCase):
pass
diff --git a/erpnext/projects/doctype/project_update/project_update.py b/erpnext/projects/doctype/project_update/project_update.py
index faa4bf1..147e591 100644
--- a/erpnext/projects/doctype/project_update/project_update.py
+++ b/erpnext/projects/doctype/project_update/project_update.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class ProjectUpdate(Document):
pass
@@ -39,4 +41,4 @@
for emails in email:
frappe.sendmail(recipients=emails,subject=frappe._(project_name + ' ' + 'Summary'),message = msg)
else:
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/projects/doctype/project_update/test_project_update.js b/erpnext/projects/doctype/project_update/test_project_update.js
deleted file mode 100644
index bda510b..0000000
--- a/erpnext/projects/doctype/project_update/test_project_update.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Project Update", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Project Update
- () => frappe.tests.make('Project Update', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/projects/doctype/project_update/test_project_update.py b/erpnext/projects/doctype/project_update/test_project_update.py
index d5d0919..1596603 100644
--- a/erpnext/projects/doctype/project_update/test_project_update.py
+++ b/erpnext/projects/doctype/project_update/test_project_update.py
@@ -3,11 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestProjectUpdate(unittest.TestCase):
pass
test_records = frappe.get_test_records('Project Update')
-test_ignore = ["Sales Order"]
\ No newline at end of file
+test_ignore = ["Sales Order"]
diff --git a/erpnext/projects/doctype/project_user/project_user.py b/erpnext/projects/doctype/project_user/project_user.py
index 3198f3b..7abe945 100644
--- a/erpnext/projects/doctype/project_user/project_user.py
+++ b/erpnext/projects/doctype/project_user/project_user.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProjectUser(Document):
pass
diff --git a/erpnext/projects/doctype/projects_settings/projects_settings.py b/erpnext/projects/doctype/projects_settings/projects_settings.py
index 9dcac14..88bb247 100644
--- a/erpnext/projects/doctype/projects_settings/projects_settings.py
+++ b/erpnext/projects/doctype/projects_settings/projects_settings.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProjectsSettings(Document):
pass
diff --git a/erpnext/projects/doctype/projects_settings/test_projects_settings.js b/erpnext/projects/doctype/projects_settings/test_projects_settings.js
deleted file mode 100644
index f6feaa4..0000000
--- a/erpnext/projects/doctype/projects_settings/test_projects_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Projects Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Projects Settings
- () => frappe.tests.make('Projects Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/projects/doctype/projects_settings/test_projects_settings.py b/erpnext/projects/doctype/projects_settings/test_projects_settings.py
index d671da7..3266246 100644
--- a/erpnext/projects/doctype/projects_settings/test_projects_settings.py
+++ b/erpnext/projects/doctype/projects_settings/test_projects_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestProjectsSettings(unittest.TestCase):
pass
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 5976e01..6b90622 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -9,7 +9,7 @@
from frappe import _, throw
from frappe.desk.form.assign_to import clear, close_all_assignments
from frappe.model.mapper import get_mapped_doc
-from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate, today, flt
+from frappe.utils import add_days, cstr, date_diff, flt, get_link_to_form, getdate, today
from frappe.utils.nestedset import NestedSet
diff --git a/erpnext/projects/doctype/task/task_tree.js b/erpnext/projects/doctype/task/task_tree.js
index d1d872f..9ebfcdd 100644
--- a/erpnext/projects/doctype/task/task_tree.js
+++ b/erpnext/projects/doctype/task/task_tree.js
@@ -81,4 +81,4 @@
}
],
extend_toolbar: true
-};
\ No newline at end of file
+};
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 0fad5e8..41a9c16 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -1,12 +1,15 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
-from frappe.utils import getdate, nowdate, add_days
+
+import frappe
+from frappe.utils import add_days, getdate, nowdate
from erpnext.projects.doctype.task.task import CircularReferenceError
+
class TestTask(unittest.TestCase):
def test_circular_reference(self):
task1 = create_task("_Test Task 1", add_days(nowdate(), -15), add_days(nowdate(), -10))
diff --git a/erpnext/projects/doctype/task_depends_on/task_depends_on.py b/erpnext/projects/doctype/task_depends_on/task_depends_on.py
index 723a0fc..ddb67ee 100644
--- a/erpnext/projects/doctype/task_depends_on/task_depends_on.py
+++ b/erpnext/projects/doctype/task_depends_on/task_depends_on.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TaskDependsOn(Document):
pass
diff --git a/erpnext/projects/doctype/task_type/task_type.py b/erpnext/projects/doctype/task_type/task_type.py
index 9c0b532..5aacf8a 100644
--- a/erpnext/projects/doctype/task_type/task_type.py
+++ b/erpnext/projects/doctype/task_type/task_type.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class TaskType(Document):
pass
diff --git a/erpnext/projects/doctype/task_type/test_task_type.py b/erpnext/projects/doctype/task_type/test_task_type.py
index 1db6e27..7690c37 100644
--- a/erpnext/projects/doctype/task_type/test_task_type.py
+++ b/erpnext/projects/doctype/task_type/test_task_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestTaskType(unittest.TestCase):
pass
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.js b/erpnext/projects/doctype/timesheet/test_timesheet.js
deleted file mode 100644
index c081d6f..0000000
--- a/erpnext/projects/doctype/timesheet/test_timesheet.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Timesheet", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Timesheet
- () => frappe.tests.make('Timesheet', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 2b0c3ab..6b32c66 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -3,21 +3,28 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
import datetime
-from frappe.utils.make_random import get_random
-from frappe.utils import now_datetime, nowdate, add_days, add_months
-from erpnext.projects.doctype.timesheet.timesheet import OverlapError
-from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
+import unittest
+
+import frappe
+from frappe.utils import add_months, now_datetime, nowdate
+
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.payroll.doctype.salary_structure.test_salary_structure \
- import make_salary_structure, create_salary_structure_assignment
-from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
- make_earning_salary_component,
- make_deduction_salary_component
-)
from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+ make_deduction_salary_component,
+ make_earning_salary_component,
+)
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
+ create_salary_structure_assignment,
+ make_salary_structure,
+)
+from erpnext.projects.doctype.timesheet.timesheet import (
+ OverlapError,
+ make_salary_slip,
+ make_sales_invoice,
+)
+
class TestTimesheet(unittest.TestCase):
@classmethod
diff --git a/erpnext/projects/doctype/timesheet/timesheet.css b/erpnext/projects/doctype/timesheet/timesheet.css
index 3a38415..1e05562 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.css
+++ b/erpnext/projects/doctype/timesheet/timesheet.css
@@ -20,4 +20,4 @@
.playpause {
border-right: 1px dashed #fff;
border-bottom: 1px dashed #fff;
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 84c7b81..1655b76 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -399,4 +399,4 @@
frappe.model.set_value(item.doctype, item.name, "project", frm.doc.parent_project);
});
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index 75f7478..be6771e 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -310,6 +310,7 @@
"read_only": 1
},
{
+ "default": "1",
"fieldname": "exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate"
@@ -319,7 +320,7 @@
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-18 16:10:08.249619",
+ "modified": "2021-06-09 12:08:53.930200",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet",
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index ae38d4c..363c3b6 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -3,19 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
import json
-from datetime import timedelta
-from erpnext.controllers.queries import get_match_cond
-from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint, date_diff, add_to_date
+
+import frappe
+from frappe import _
from frappe.model.document import Document
-from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
- WorkstationHolidayError)
-from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
-from erpnext.setup.utils import get_exchange_rate
+from frappe.utils import flt, getdate, time_diff_in_hours
+
+from erpnext.controllers.queries import get_match_cond
from erpnext.hr.utils import validate_active_employee
+from erpnext.setup.utils import get_exchange_rate
+
class OverlapError(frappe.ValidationError): pass
class OverWorkLoggedError(frappe.ValidationError): pass
@@ -216,24 +215,60 @@
@frappe.whitelist()
def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
- condition = ''
+ condition = ""
if project:
- condition += "and tsd.project = %(project)s"
+ condition += "AND tsd.project = %(project)s "
if parent:
- condition += "AND tsd.parent = %(parent)s"
+ condition += "AND tsd.parent = %(parent)s "
if from_time and to_time:
condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
- return frappe.db.sql("""SELECT tsd.name as name,
- tsd.parent as parent, tsd.billing_hours as billing_hours,
- tsd.billing_amount as billing_amount, tsd.activity_type as activity_type,
- tsd.description as description, ts.currency as currency
- FROM `tabTimesheet Detail` tsd
- INNER JOIN `tabTimesheet` ts ON ts.name = tsd.parent
- WHERE tsd.parenttype = 'Timesheet'
- and tsd.docstatus=1 {0}
- and tsd.is_billable = 1
- and tsd.sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
+ query = f"""
+ SELECT
+ tsd.name as name,
+ tsd.parent as time_sheet,
+ tsd.from_time as from_time,
+ tsd.to_time as to_time,
+ tsd.billing_hours as billing_hours,
+ tsd.billing_amount as billing_amount,
+ tsd.activity_type as activity_type,
+ tsd.description as description,
+ ts.currency as currency,
+ tsd.project_name as project_name
+ FROM `tabTimesheet Detail` tsd
+ INNER JOIN `tabTimesheet` ts
+ ON ts.name = tsd.parent
+ WHERE
+ tsd.parenttype = 'Timesheet'
+ AND tsd.docstatus = 1
+ AND tsd.is_billable = 1
+ AND tsd.sales_invoice is NULL
+ {condition}
+ ORDER BY tsd.from_time ASC
+ """
+
+ 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,
+ 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]
+
+ if timelog_detail.currency:
+ exchange_rate = get_exchange_rate(timelog_detail.currency, currency)
+
+ return timelog_detail.billing_amount * exchange_rate
+ return timelog_detail.billing_amount
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/projects/doctype/timesheet/timesheet_calendar.js b/erpnext/projects/doctype/timesheet/timesheet_calendar.js
index 14f016a..80967ed 100644
--- a/erpnext/projects/doctype/timesheet/timesheet_calendar.js
+++ b/erpnext/projects/doctype/timesheet/timesheet_calendar.js
@@ -9,8 +9,8 @@
"title": "title"
},
style_map: {
- "0": "info",
- "1": "standard",
+ "0": "info",
+ "1": "standard",
"2": "danger"
},
gantt: true,
diff --git a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
index acff97a..3ef1d92 100644
--- a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
+++ b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'time_sheet',
@@ -10,4 +12,4 @@
'items': ['Sales Invoice', 'Salary Slip']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/projects/doctype/timesheet/timesheet_list.js b/erpnext/projects/doctype/timesheet/timesheet_list.js
index 1b200f8..b59fdc9 100644
--- a/erpnext/projects/doctype/timesheet/timesheet_list.js
+++ b/erpnext/projects/doctype/timesheet/timesheet_list.js
@@ -4,13 +4,13 @@
if (doc.status== "Billed") {
return [__("Billed"), "green", "status,=," + "Billed"]
}
-
+
if (doc.status== "Payslip") {
return [__("Payslip"), "green", "status,=," + "Payslip"]
}
-
+
if (doc.status== "Completed") {
return [__("Completed"), "green", "status,=," + "Completed"]
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
index 7da94b7..4fd233e 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class TimesheetDetail(Document):
pass
diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py
index 5efde41..dec2824 100644
--- a/erpnext/projects/report/billing_summary.py
+++ b/erpnext/projects/report/billing_summary.py
@@ -3,9 +3,11 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import time_diff_in_hours, flt
+from frappe.utils import flt, time_diff_in_hours
+
def get_columns():
return [
@@ -144,4 +146,4 @@
if activity_duration != activity.billing_hours:
billing_duration = activity_duration * activity.billing_hours / activity.hours
- return flt(activity_duration, precision), flt(billing_duration, precision)
\ No newline at end of file
+ return flt(activity_duration, precision), flt(billing_duration, precision)
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 682fb2e..3a33b4b 100644
--- a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
+++ b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.desk.reportview import build_match_conditions
+
def execute(filters=None):
if not filters:
filters = {}
@@ -20,8 +22,8 @@
return columns, data
def get_column():
- return [_("Timesheet") + ":Link/Timesheet:120", _("Employee") + "::150", _("Employee Name") + "::150",
- _("From Datetime") + "::140", _("To Datetime") + "::140", _("Hours") + "::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"]
@@ -45,4 +47,4 @@
if match_conditions:
conditions += " and %s" % match_conditions
- return conditions
\ No newline at end of file
+ return conditions
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 cdabe64..3016390 100644
--- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
+++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils import date_diff, nowdate
+
def execute(filters=None):
columns, data = [], []
data = get_data(filters)
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 dbeedb4..88c77c8 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
@@ -1,16 +1,20 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from frappe.utils import nowdate, add_days, add_months
+from frappe.utils import add_days, add_months, nowdate
+
from erpnext.projects.doctype.task.test_task import create_task
from erpnext.projects.report.delayed_tasks_summary.delayed_tasks_summary import execute
+
class TestDelayedTasksSummary(unittest.TestCase):
@classmethod
def setUp(self):
task1 = create_task("_Test Task 98", add_days(nowdate(), -10), nowdate())
create_task("_Test Task 99", add_days(nowdate(), -10), add_days(nowdate(), -1))
-
+
task1.status = "Completed"
task1.completed_on = add_days(nowdate(), -1)
task1.save()
@@ -38,7 +42,7 @@
]
report = execute(filters)
data = list(filter(lambda x: x.subject == "_Test Task 99", report[1]))[0]
-
+
for key in ["subject", "status", "priority", "delay"]:
self.assertEqual(expected_data[0].get(key), data.get(key))
@@ -51,4 +55,4 @@
def tearDown(self):
for task in ["_Test Task 98", "_Test Task 99"]:
- frappe.get_doc("Task", {"subject": task}).delete()
\ No newline at end of file
+ frappe.get_doc("Task", {"subject": task}).delete()
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
index cd5ad78..30bd9f0 100644
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
+++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
@@ -2,13 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _
+
from erpnext.projects.report.billing_summary import get_columns, get_data
+
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns()
data = get_data(filters)
- return columns, data
\ No newline at end of file
+ return columns, data
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 4d22f46..d59a2ac 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
@@ -2,11 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt, getdate
from six import iteritems
+
def execute(filters=None):
return EmployeeHoursReport(filters).run()
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 0e5a597..f456c84 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
@@ -1,11 +1,16 @@
from __future__ import unicode_literals
-import unittest
-import frappe
+import unittest
+
+import frappe
from frappe.utils.make_random import get_random
-from erpnext.projects.report.employee_hours_utilization_based_on_timesheet.employee_hours_utilization_based_on_timesheet import execute
+
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.projects.doctype.project.test_project import make_project
+from erpnext.projects.report.employee_hours_utilization_based_on_timesheet.employee_hours_utilization_based_on_timesheet import (
+ execute,
+)
+
class TestEmployeeUtilization(unittest.TestCase):
@classmethod
@@ -195,4 +200,4 @@
'per_util': 27.78,
'per_util_billed_only': 27.78
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.py b/erpnext/projects/report/project_billing_summary/project_billing_summary.py
index cd5ad78..30bd9f0 100644
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.py
+++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.py
@@ -2,13 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _
+
from erpnext.projects.report.billing_summary import get_columns, get_data
+
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns()
data = get_data(filters)
- return columns, data
\ No newline at end of file
+ return columns, data
diff --git a/erpnext/projects/report/project_profitability/project_profitability.py b/erpnext/projects/report/project_profitability/project_profitability.py
index 9139d84..13e02c8 100644
--- a/erpnext/projects/report/project_profitability/project_profitability.py
+++ b/erpnext/projects/report/project_profitability/project_profitability.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
columns, data = [], []
data = get_data(filters)
@@ -208,4 +210,4 @@
"options": "Currency",
"width": 80
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index 180926f..8cf169b 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -1,12 +1,19 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from frappe.utils import getdate, nowdate, add_days
+from frappe.utils import add_days, getdate, nowdate
+
from erpnext.hr.doctype.employee.test_employee import make_employee
-from erpnext.projects.doctype.timesheet.test_timesheet import make_salary_structure_for_timesheet, make_timesheet
+from erpnext.projects.doctype.timesheet.test_timesheet import (
+ make_salary_structure_for_timesheet,
+ make_timesheet,
+)
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
from erpnext.projects.report.project_profitability.project_profitability import execute
+
class TestProjectProfitability(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py
index 98dd617..dbb4e84 100644
--- a/erpnext/projects/report/project_summary/project_summary.py
+++ b/erpnext/projects/report/project_summary/project_summary.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns()
data = []
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 c13d7cf..5d2b7db 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
@@ -5,6 +5,7 @@
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns()
proj_details = get_project_details()
diff --git a/erpnext/projects/utils.py b/erpnext/projects/utils.py
index c39f908..6926470 100644
--- a/erpnext/projects/utils.py
+++ b/erpnext/projects/utils.py
@@ -4,8 +4,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def query_task(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/projects/web_form/tasks/tasks.js b/erpnext/projects/web_form/tasks/tasks.js
index 699703c..ffc5e98 100644
--- a/erpnext/projects/web_form/tasks/tasks.js
+++ b/erpnext/projects/web_form/tasks/tasks.js
@@ -1,3 +1,3 @@
frappe.ready(function() {
// bind events here
-})
\ No newline at end of file
+})
diff --git a/erpnext/projects/web_form/tasks/tasks.py b/erpnext/projects/web_form/tasks/tasks.py
index e97f36d..aed7942 100644
--- a/erpnext/projects/web_form/tasks/tasks.py
+++ b/erpnext/projects/web_form/tasks/tasks.py
@@ -2,11 +2,12 @@
import frappe
+
def get_context(context):
if 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}]
context.success_url = "/projects?project=" + context.doc.project
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 7a3cb83..6b70dab 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -3,7 +3,8 @@
"public/less/erpnext.less",
"public/less/hub.less",
"public/scss/call_popup.scss",
- "public/scss/point-of-sale.scss"
+ "public/scss/point-of-sale.scss",
+ "public/scss/hierarchy_chart.scss"
],
"css/marketplace.css": [
"public/less/hub.less"
@@ -37,13 +38,15 @@
"public/js/templates/item_quick_entry.html",
"public/js/utils/item_quick_entry.js",
"public/js/utils/customer_quick_entry.js",
+ "public/js/utils/supplier_quick_entry.js",
"public/js/education/student_button.html",
"public/js/education/assessment_result_tool.html",
"public/js/hub/hub_factory.js",
"public/js/call_popup/call_popup.js",
"public/js/utils/dimension_tree_filter.js",
"public/js/telephony.js",
- "public/js/templates/call_link.html"
+ "public/js/templates/call_link.html",
+ "public/js/templates/node_card.html"
],
"js/item-dashboard.min.js": [
"stock/dashboard/item_dashboard.html",
@@ -66,5 +69,9 @@
"public/js/bank_reconciliation_tool/data_table_manager.js",
"public/js/bank_reconciliation_tool/number_card.js",
"public/js/bank_reconciliation_tool/dialog_manager.js"
+ ],
+ "js/hierarchy-chart.min.js": [
+ "public/js/hierarchy_chart/hierarchy_chart_desktop.js",
+ "public/js/hierarchy_chart/hierarchy_chart_mobile.js"
]
}
diff --git a/erpnext/public/images/erpnext-favicon.svg b/erpnext/public/images/erpnext-favicon.svg
index a3ac3bb..6bc6b2c 100644
--- a/erpnext/public/images/erpnext-favicon.svg
+++ b/erpnext/public/images/erpnext-favicon.svg
@@ -2,4 +2,4 @@
<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#0089FF"/>
<path d="M65.7097 32.9462H67.3871V24H33V32.9462H43.9032H65.7097Z" fill="white"/>
<path d="M43.9032 66.2151V53.914H65.7097V44.9677H43.9032H33V75.1613H67.6667V66.2151H43.9032Z" fill="white"/>
-</svg>
\ No newline at end of file
+</svg>
diff --git a/erpnext/public/images/erpnext-logo.svg b/erpnext/public/images/erpnext-logo.svg
index a3ac3bb..6bc6b2c 100644
--- a/erpnext/public/images/erpnext-logo.svg
+++ b/erpnext/public/images/erpnext-logo.svg
@@ -2,4 +2,4 @@
<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#0089FF"/>
<path d="M65.7097 32.9462H67.3871V24H33V32.9462H43.9032H65.7097Z" fill="white"/>
<path d="M43.9032 66.2151V53.914H65.7097V44.9677H43.9032H33V75.1613H67.6667V66.2151H43.9032Z" fill="white"/>
-</svg>
\ No newline at end of file
+</svg>
diff --git a/erpnext/public/images/pos.svg b/erpnext/public/images/pos.svg
index 3d12d9c..90714e9 100644
--- a/erpnext/public/images/pos.svg
+++ b/erpnext/public/images/pos.svg
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<rect fill="none" width="100" height="100"/>
<path d="M73.617,42.921H53.671c-0.788,0-1.423,0.636-1.423,1.423v7.238c0,0.788,0.635,1.424,1.423,1.424h19.946 c0.788,0,1.429-0.636,1.429-1.424v-7.238C75.046,43.557,74.405,42.921,73.617,42.921z M72.194,50.16H55.099v-4.392h17.096V50.16z M58.494,57.78c0,0.788-0.635,1.423-1.423,1.423h-3.4c-0.788,0-1.423-0.635-1.423-1.423s0.635-1.429,1.423-1.429h3.4 C57.859,56.352,58.494,56.992,58.494,57.78z M66.771,57.78c0,0.788-0.636,1.423-1.424,1.423h-3.4c-0.788,0-1.429-0.635-1.429-1.423 s0.641-1.429,1.429-1.429h3.4C66.135,56.352,66.771,56.992,66.771,57.78z M75.046,57.78c0,0.788-0.641,1.423-1.429,1.423h-3.396 c-0.788,0-1.423-0.635-1.423-1.423s0.635-1.429,1.423-1.429h3.396C74.405,56.352,75.046,56.992,75.046,57.78z M58.494,64.058 c0,0.788-0.635,1.429-1.423,1.429h-3.4c-0.788,0-1.423-0.641-1.423-1.429s0.635-1.423,1.423-1.423h3.4 C57.859,62.635,58.494,63.27,58.494,64.058z M66.771,64.058c0,0.788-0.636,1.429-1.424,1.429h-3.4c-0.788,0-1.429-0.641-1.429-1.429 s0.641-1.423,1.429-1.423h3.4C66.135,62.635,66.771,63.27,66.771,64.058z M75.046,64.058c0,0.788-0.641,1.429-1.429,1.429h-3.396 c-0.788,0-1.423-0.641-1.423-1.429s0.635-1.423,1.423-1.423h3.396C74.405,62.635,75.046,63.27,75.046,64.058z M87.271,72.227 c-0.239-13.364-2.135-26.347-5.562-37.713c-0.366-1.2-1.474-2.028-2.729-2.028H66.496V22.334h11.636c1.57,0,2.847-1.276,2.847-2.852 V7.847C80.979,6.276,79.702,5,78.132,5H49.163c-1.576,0-2.852,1.276-2.852,2.847v11.636c0,1.576,1.276,2.852,2.852,2.852h11.634 v10.151H44.255c0.902-1.006,1.896-1.789,2.977-2.257c0.623-0.264,0.971-0.93,0.833-1.591c-0.137-0.666-0.722-1.139-1.398-1.139 H33.028c-0.193,0-0.386,0.041-0.564,0.117c-2.104,0.91-3.896,2.587-5.315,4.87h-6.133c-1.253,0-2.364,0.829-2.725,2.028 c-3.431,11.366-5.327,24.349-5.566,37.713c-1.515,0.066-2.732,1.297-2.732,2.826v17.096c0,1.575,1.276,2.852,2.849,2.852h74.312 c1.576,0,2.852-1.276,2.852-2.852V75.053C90.006,73.523,88.791,72.293,87.271,72.227z M52.012,10.698h23.268v5.938H52.012V10.698z M33.338,30.351h8.998c-2.229,2.389-3.922,5.901-4.85,10.182c-1.281,5.922-0.854,11.94,0.976,15.819H29 c-2.336-3.151-3.134-10.07-1.807-16.201C28.248,35.276,30.482,31.723,33.338,30.351z M23.157,38.189h1.604 c-0.122,0.447-0.251,0.884-0.353,1.357c-1.332,6.161-0.732,12.663,1.332,16.805h-1.271c-0.788,0-1.426,0.641-1.426,1.429 s0.638,1.423,1.426,1.423h3.858H41.06h3.116c0.788,0,1.423-0.635,1.423-1.423s-0.636-1.429-1.423-1.429h-2.389 c-2.14-2.897-2.809-9.241-1.515-15.214c0.227-1.052,0.516-2.018,0.834-2.948H76.84c2.903,10.339,4.529,22.005,4.753,34.012H18.404 C18.627,60.194,20.257,48.528,23.157,38.189z M84.308,89.302H15.694V77.899h68.613V89.302z M45.45,83.598 c0-1.57,1.276-2.846,2.849-2.846h3.4c1.575,0,2.851,1.275,2.851,2.846c0,1.576-1.275,2.853-2.851,2.853h-3.4 C46.726,86.45,45.45,85.174,45.45,83.598z"/>
-</svg>
\ No newline at end of file
+</svg>
diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
index 239fbb9..ca73393 100644
--- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
+++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
@@ -227,7 +227,7 @@
{
fieldtype: "HTML",
fieldname: "no_matching_vouchers",
- options: "<div class='text-muted text-center'>No Matching Vouchers Found</div>"
+ options: "<div class=\"text-muted text-center\">No Matching Vouchers Found</div>"
},
{
fieldtype: "Section Break",
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 84697e0..702064f 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -47,7 +47,10 @@
if (in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) && this.frm.doc.is_pos &&
this.frm.doc.is_return) {
- this.update_paid_amount_for_return();
+ if (this.frm.doc.doctype == "Sales Invoice") {
+ this.set_total_amount_to_default_mop();
+ }
+ this.calculate_paid_amount();
}
// Sales person's commission
@@ -67,8 +70,6 @@
calculate_discount_amount() {
if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
- this.calculate_item_values();
- this.calculate_net_total();
this.set_discount_amount();
this.apply_discount_amount();
}
@@ -734,7 +735,7 @@
}
}
- update_paid_amount_for_return() {
+ set_total_amount_to_default_mop() {
var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
@@ -747,15 +748,12 @@
precision("base_grand_total")
);
}
-
this.frm.doc.payments.find(pay => {
if (pay.default) {
pay.amount = total_amount_to_pay;
}
});
this.frm.refresh_fields();
-
- this.calculate_paid_amount();
}
set_default_payment(total_amount_to_pay, update_paid_amount) {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3c6c347..63fd8a1 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -342,53 +342,17 @@
this.set_dynamic_labels();
this.setup_sms();
this.setup_quality_inspection();
- let scan_barcode_field = this.frm.get_field('scan_barcode');
- if (scan_barcode_field && scan_barcode_field.get_value()) {
- scan_barcode_field.set_value("");
- scan_barcode_field.set_new_description("");
-
- if (frappe.is_mobile()) {
- if (scan_barcode_field.$input_wrapper.find('.input-group').length) return;
-
- let $input_group = $('<div class="input-group">');
- scan_barcode_field.$input_wrapper.find('.control-input').append($input_group);
- $input_group.append(scan_barcode_field.$input);
- $(`<span class="input-group-btn" style="vertical-align: top">
- <button class="btn btn-default border" type="button">
- <i class="fa fa-camera text-muted"></i>
- </button>
- </span>`)
- .on('click', '.btn', () => {
- frappe.barcode.scan_barcode().then(barcode => {
- scan_barcode_field.set_value(barcode);
- });
- })
- .appendTo($input_group);
- }
- }
}
scan_barcode() {
- let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
-
- let show_description = function(idx, exist = null) {
- 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'
- });
- }
- }
+ 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 }
+ args: {
+ search_value: this.frm.doc.scan_barcode
+ }
}).then(r => {
const data = r && r.message;
if (!data || Object.keys(data).length === 0) {
@@ -399,49 +363,96 @@
return;
}
- let cur_grid = this.frm.fields_dict.items.grid;
-
- let row_to_modify = null;
- 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;
- }
-
- if (!row_to_modify) {
- // add new row
- row_to_modify = frappe.model.add_child(this.frm.doc, cur_grid.doctype, 'items');
- }
-
- show_description(row_to_modify.idx, row_to_modify.item_code);
-
- 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 value = (row_to_modify[field] && field === "serial_no")
- ? row_to_modify[field] + '\n' + data[field] : data[field];
-
- frappe.model.set_value(row_to_modify.doctype,
- row_to_modify.name, field, value);
- }
- });
-
- scan_barcode_field.set_value('');
- refresh_field("items");
+ 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'
+ });
+ }
+ }
+
apply_default_taxes() {
var me = this;
var taxes_and_charges_field = frappe.meta.get_docfield(me.frm.doc.doctype, "taxes_and_charges",
@@ -511,6 +522,10 @@
var me = this;
var item = frappe.get_doc(cdt, cdn);
var update_stock = 0, show_batch_dialog = 0;
+
+ item.weight_per_unit = 0;
+ item.weight_uom = '';
+
if(['Sales Invoice'].includes(this.frm.doc.doctype)) {
update_stock = cint(me.frm.doc.update_stock);
show_batch_dialog = update_stock;
@@ -637,6 +652,7 @@
me.frm.script_manager.trigger('qty', item.doctype, item.name);
if (!me.frm.doc.set_warehouse)
me.frm.script_manager.trigger('warehouse', item.doctype, item.name);
+ me.apply_price_list(item, true);
}, undefined, !frappe.flags.hide_serial_batch_dialog);
}
},
@@ -890,21 +906,27 @@
if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
- erpnext.utils.get_shipping_address(this.frm, function(){
+ erpnext.utils.get_shipping_address(this.frm, function() {
set_party_account(set_pricing);
});
// Get default company billing address in Purchase Invoice, Order and Receipt
- frappe.call({
- 'method': 'frappe.contacts.doctype.address.address.get_default_address',
- 'args': {
- 'doctype': 'Company',
- 'name': this.frm.doc.company
- },
- 'callback': function(r) {
- me.frm.set_value('billing_address', r.message);
- }
- });
+ if (this.frm.doc.company && frappe.meta.get_docfield(this.frm.doctype, "billing_address")) {
+ frappe.call({
+ method: "erpnext.setup.doctype.company.company.get_default_company_address",
+ args: {name: this.frm.doc.company, existing_address: this.frm.doc.billing_address || ""},
+ debounce: 2000,
+ callback: function(r) {
+ if (r.message) {
+ me.frm.set_value("billing_address", r.message);
+ } else {
+ if (frappe.meta.get_docfield(me.frm.doctype, 'company_address')) {
+ me.frm.set_value("company_address", "");
+ }
+ }
+ }
+ });
+ }
} else {
set_party_account(set_pricing);
@@ -2266,12 +2288,19 @@
coupon_code() {
var me = this;
- frappe.run_serially([
- () => this.frm.doc.ignore_pricing_rule=1,
- () => me.ignore_pricing_rule(),
- () => this.frm.doc.ignore_pricing_rule=0,
- () => me.apply_pricing_rule()
- ]);
+ if (this.frm.doc.coupon_code) {
+ frappe.run_serially([
+ () => this.frm.doc.ignore_pricing_rule=1,
+ () => me.ignore_pricing_rule(),
+ () => this.frm.doc.ignore_pricing_rule=0,
+ () => me.apply_pricing_rule()
+ ]);
+ } else {
+ frappe.run_serially([
+ () => this.frm.doc.ignore_pricing_rule=1,
+ () => me.ignore_pricing_rule()
+ ]);
+ }
}
};
diff --git a/erpnext/public/js/education/assessment_result_tool.html b/erpnext/public/js/education/assessment_result_tool.html
index b591010..f7d1ab3 100644
--- a/erpnext/public/js/education/assessment_result_tool.html
+++ b/erpnext/public/js/education/assessment_result_tool.html
@@ -69,4 +69,4 @@
</tr>
{% endfor %}
</tbody>
-</table>
\ No newline at end of file
+</table>
diff --git a/erpnext/public/js/education/student_button.html b/erpnext/public/js/education/student_button.html
index 3cf2592..b64c73a 100644
--- a/erpnext/public/js/education/student_button.html
+++ b/erpnext/public/js/education/student_button.html
@@ -1,12 +1,12 @@
<div class="col-sm-3">
<div class="checkbox">
<label>
- <input
+ <input
type="checkbox"
- data-group_roll_number="{{group_roll_number}}"
+ data-group_roll_number="{{group_roll_number}}"
data-student="{{student}}"
data-student-name="{{student_name}}"
- class="students-check"
+ class="students-check"
{% if status === "Present" %}
checked
{% endif %}
@@ -14,4 +14,4 @@
{{ group_roll_number }} - {{ student_name }}
</label>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index 519cfca..febdb24 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -15,6 +15,7 @@
import "./templates/item_quick_entry.html";
import "./utils/item_quick_entry";
import "./utils/customer_quick_entry";
+import "./utils/supplier_quick_entry";
import "./education/student_button.html";
import "./education/assessment_result_tool.html";
import "./hub/hub_factory";
@@ -24,4 +25,3 @@
import "./templates/call_link.html";
// import { sum } from 'frappe/public/utils/util.js'
-
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index b2f7afe..0d79b10 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -176,5 +176,3 @@
return filters;
}
-
-
diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js
index d0c935f..b643cca 100644
--- a/erpnext/public/js/help_links.js
+++ b/erpnext/public/js/help_links.js
@@ -5,7 +5,7 @@
frappe.help.help_links["Form/Rename Tool"] = [
{
label: "Bulk Rename",
- url: docsUrl + "user/manual/en/setting-up/data/bulk-rename",
+ url: docsUrl + "user/manual/en/using-erpnext/articles/bulk-rename",
},
];
@@ -59,10 +59,23 @@
},
];
-frappe.help.help_links["data-import-tool"] = [
+frappe.help.help_links["Form/Data Import"] = [
{
label: "Importing and Exporting Data",
- url: docsUrl + "user/manual/en/setting-up/data/data-import-tool",
+ url: docsUrl + "user/manual/en/setting-up/data/data-import",
+ },
+ {
+ label: "Overwriting Data from Data Import Tool",
+ url:
+ docsUrl +
+ "user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool",
+ },
+];
+
+frappe.help.help_links["List/Data Import"] = [
+ {
+ label: "Importing and Exporting Data",
+ url: docsUrl + "user/manual/en/setting-up/data/data-import",
},
{
label: "Overwriting Data from Data Import Tool",
@@ -101,14 +114,14 @@
},
];
-frappe.help.help_links["Form/Email Digest"] = [
+frappe.help.help_links["List/Print Heading"] = [
{
- label: "Email Digest",
- url: docsUrl + "user/manual/en/setting-up/email/email-digest",
+ label: "Print Heading",
+ url: docsUrl + "user/manual/en/setting-up/print/print-headings",
},
];
-frappe.help.help_links["List/Print Heading"] = [
+frappe.help.help_links["Form/Print Heading"] = [
{
label: "Print Heading",
url: docsUrl + "user/manual/en/setting-up/print/print-headings",
@@ -153,18 +166,25 @@
frappe.help.help_links["List/Notification"] = [
{
label: "Notification",
- url: docsUrl + "user/manual/en/setting-up/email/notifications",
+ url: docsUrl + "user/manual/en/setting-up/notifications",
},
];
frappe.help.help_links["Form/Notification"] = [
{
label: "Notification",
- url: docsUrl + "user/manual/en/setting-up/email/notifications",
+ url: docsUrl + "user/manual/en/setting-up/notifications",
},
];
-frappe.help.help_links["List/Email Digest"] = [
+frappe.help.help_links["Form/Email Digest"] = [
+ {
+ label: "Email Digest",
+ url: docsUrl + "user/manual/en/setting-up/email/email-digest",
+ },
+];
+
+frappe.help.help_links["Form/Email Digest"] = [
{
label: "Email Digest",
url: docsUrl + "user/manual/en/setting-up/email/email-digest",
@@ -174,7 +194,7 @@
frappe.help.help_links["List/Auto Email Report"] = [
{
label: "Auto Email Reports",
- url: docsUrl + "user/manual/en/setting-up/email/email-reports",
+ url: docsUrl + "user/manual/en/setting-up/email/auto-email-reports",
},
];
@@ -188,14 +208,7 @@
frappe.help.help_links["print-format-builder"] = [
{
label: "Print Format Builder",
- url: docsUrl + "user/manual/en/setting-up/print/print-settings",
- },
-];
-
-frappe.help.help_links["List/Print Heading"] = [
- {
- label: "Print Heading",
- url: docsUrl + "user/manual/en/setting-up/print/print-headings",
+ url: docsUrl + "user/manual/en/setting-up/print/print-format-builder",
},
];
@@ -300,7 +313,7 @@
},
{
label: "Recurring Sales Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
{
label: "Applying Discount",
@@ -315,7 +328,7 @@
},
{
label: "Recurring Sales Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
{
label: "Applying Discount",
@@ -344,14 +357,14 @@
frappe.help.help_links["Form/Product Bundle"] = [
{
label: "Product Bundle",
- url: docsUrl + "user/manual/en/selling/setup/product-bundle",
+ url: docsUrl + "user/manual/en/selling/product-bundle",
},
];
frappe.help.help_links["Form/Selling Settings"] = [
{
label: "Selling Settings",
- url: docsUrl + "user/manual/en/selling/setup/selling-settings",
+ url: docsUrl + "user/manual/en/selling/selling-settings",
},
];
@@ -397,7 +410,7 @@
},
{
label: "Recurring Purchase Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
@@ -420,7 +433,7 @@
},
{
label: "Recurring Purchase Order",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
{
label: "Subcontracting",
@@ -435,24 +448,17 @@
},
];
-frappe.help.help_links["List/POS Profile"] = [
- {
- label: "POS Profile",
- url: docsUrl + "user/manual/en/setting-up/pos-setting",
- },
-];
-
frappe.help.help_links["List/Price List"] = [
{
label: "Price List",
- url: docsUrl + "user/manual/en/setting-up/price-lists",
+ url: docsUrl + "user/manual/en/stock/price-lists",
},
];
frappe.help.help_links["List/Authorization Rule"] = [
{
label: "Authorization Rule",
- url: docsUrl + "user/manual/en/setting-up/authorization-rule",
+ url: docsUrl + "user/manual/en/customize-erpnext/authorization-rule",
},
];
@@ -468,27 +474,14 @@
label: "Stock Reconciliation",
url:
docsUrl +
- "user/manual/en/setting-up/stock-reconciliation-for-non-serialized-item",
+ "user/manual/en/stock/stock-reconciliation",
},
];
frappe.help.help_links["Tree/Territory"] = [
{
label: "Territory",
- url: docsUrl + "user/manual/en/setting-up/territory",
- },
-];
-
-frappe.help.help_links["Form/Dropbox Backup"] = [
- {
- label: "Dropbox Backup",
- url: docsUrl + "user/manual/en/setting-up/third-party-backups",
- },
- {
- label: "Setting Up Dropbox Backup",
- url:
- docsUrl +
- "user/manual/en/setting-up/articles/setting-up-dropbox-backups",
+ url: docsUrl + "user/manual/en/selling/territory",
},
];
@@ -502,12 +495,6 @@
url: docsUrl + "user/manual/en/setting-up/company-setup",
},
{
- label: "Managing Multiple Companies",
- url:
- docsUrl +
- "user/manual/en/setting-up/articles/managing-multiple-companies",
- },
- {
label: "Delete All Related Transactions for a Company",
url:
docsUrl +
@@ -517,21 +504,6 @@
//Accounts
-frappe.help.help_links["modules/Accounts"] = [
- {
- label: "Introduction to Accounts",
- url: docsUrl + "user/manual/en/accounts/",
- },
- {
- label: "Chart of Accounts",
- url: docsUrl + "user/manual/en/accounts/chart-of-accounts.html",
- },
- {
- label: "Multi Currency Accounting",
- url: docsUrl + "user/manual/en/accounts/multi-currency-accounting",
- },
-];
-
frappe.help.help_links["Tree/Account"] = [
{
label: "Chart of Accounts",
@@ -552,7 +524,7 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balance",
},
{
label: "Sales Return",
@@ -560,7 +532,7 @@
},
{
label: "Recurring Sales Invoice",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
@@ -571,7 +543,7 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balances",
},
{
label: "Sales Return",
@@ -579,21 +551,28 @@
},
{
label: "Recurring Sales Invoice",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
-frappe.help.help_links["pos"] = [
+frappe.help.help_links["point-of-sale"] = [
{
label: "Point of Sale Invoice",
- url: docsUrl + "user/manual/en/accounts/point-of-sale-pos-invoice",
+ url: docsUrl + "user/manual/en/accounts/point-of-sales",
},
];
frappe.help.help_links["List/POS Profile"] = [
{
label: "Point of Sale Profile",
- url: docsUrl + "user/manual/en/setting-up/pos-setting",
+ url: docsUrl + "user/manual/en/accounts/pos-profile",
+ },
+];
+
+frappe.help.help_links["Form/POS Profile"] = [
+ {
+ label: "POS Profile",
+ url: docsUrl + "user/manual/en/accounts/pos-profile",
},
];
@@ -604,11 +583,11 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balance",
},
{
label: "Recurring Purchase Invoice",
- url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+ url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
},
];
@@ -623,7 +602,7 @@
},
{
label: "Accounts Opening Balance",
- url: docsUrl + "user/manual/en/accounts/opening-accounts",
+ url: docsUrl + "user/manual/en/accounts/opening-balance",
},
];
@@ -644,7 +623,7 @@
frappe.help.help_links["List/Asset"] = [
{
label: "Managing Fixed Assets",
- url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+ url: docsUrl + "user/manual/en/asset",
},
];
@@ -659,6 +638,8 @@
{ label: "Budgeting", url: docsUrl + "user/manual/en/accounts/budgeting" },
];
+//Stock
+
frappe.help.help_links["List/Item"] = [
{ label: "Item", url: docsUrl + "user/manual/en/stock/item" },
{
@@ -676,7 +657,7 @@
},
{
label: "Managing Fixed Assets",
- url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+ url: docsUrl + "user/manual/en/asset",
},
{
label: "Item Codification",
@@ -711,7 +692,7 @@
},
{
label: "Managing Fixed Assets",
- url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+ url: docsUrl + "user/manual/en/asset",
},
{
label: "Item Codification",
@@ -771,10 +752,6 @@
url:
docsUrl + "user/manual/en/stock/articles/track-items-using-barcode",
},
- {
- label: "Subcontracting",
- url: docsUrl + "user/manual/en/manufacturing/subcontracting",
- },
];
frappe.help.help_links["List/Installation Note"] = [
@@ -784,21 +761,10 @@
},
];
-frappe.help.help_links["Tree"] = [
- {
- label: "Managing Tree Structure Masters",
- url:
- docsUrl +
- "user/manual/en/setting-up/articles/managing-tree-structure-masters",
- },
-];
-
frappe.help.help_links["List/Budget"] = [
{ label: "Budgeting", url: docsUrl + "user/manual/en/accounts/budgeting" },
];
-//Stock
-
frappe.help.help_links["List/Material Request"] = [
{
label: "Material Request",
@@ -861,6 +827,10 @@
{ label: "Serial No", url: docsUrl + "user/manual/en/stock/serial-no" },
];
+frappe.help.help_links["List/Batch"] = [
+ { label: "Batch", url: docsUrl + "user/manual/en/stock/batch" },
+];
+
frappe.help.help_links["Form/Batch"] = [
{ label: "Batch", url: docsUrl + "user/manual/en/stock/batch" },
];
@@ -868,35 +838,35 @@
frappe.help.help_links["Form/Packing Slip"] = [
{
label: "Packing Slip",
- url: docsUrl + "user/manual/en/stock/tools/packing-slip",
+ url: docsUrl + "user/manual/en/stock/packing-slip",
},
];
frappe.help.help_links["Form/Quality Inspection"] = [
{
label: "Quality Inspection",
- url: docsUrl + "user/manual/en/stock/tools/quality-inspection",
+ url: docsUrl + "user/manual/en/stock/quality-inspection",
},
];
frappe.help.help_links["Form/Landed Cost Voucher"] = [
{
label: "Landed Cost Voucher",
- url: docsUrl + "user/manual/en/stock/tools/landed-cost-voucher",
+ url: docsUrl + "user/manual/en/stock/landed-cost-voucher",
},
];
frappe.help.help_links["Tree/Item Group"] = [
{
label: "Item Group",
- url: docsUrl + "user/manual/en/stock/setup/item-group",
+ url: docsUrl + "user/manual/en/stock/item-group",
},
];
frappe.help.help_links["Form/Item Attribute"] = [
{
label: "Item Attribute",
- url: docsUrl + "user/manual/en/stock/setup/item-attribute",
+ url: docsUrl + "user/manual/en/stock/item-attribute",
},
];
@@ -911,7 +881,7 @@
frappe.help.help_links["Form/Stock Reconciliation"] = [
{
label: "Opening Stock Entry",
- url: docsUrl + "user/manual/en/stock/opening-stock",
+ url: docsUrl + "user/manual/en/stock/stock-reconciliation",
},
];
@@ -938,13 +908,13 @@
];
frappe.help.help_links["Form/Campaign"] = [
- { label: "Campaign", url: docsUrl + "user/manual/en/CRM/setup/campaign" },
+ { label: "Campaign", url: docsUrl + "user/manual/en/CRM/campaign" },
];
frappe.help.help_links["Tree/Sales Person"] = [
{
label: "Sales Person",
- url: docsUrl + "user/manual/en/CRM/setup/sales-person",
+ url: docsUrl + "user/manual/en/CRM/sales-person",
},
];
@@ -953,30 +923,13 @@
label: "Sales Person Target",
url:
docsUrl +
- "user/manual/en/selling/setup/sales-person-target-allocation",
+ "user/manual/en/selling/sales-person-target-allocation",
},
-];
-
-//Support
-
-frappe.help.help_links["List/Feedback Trigger"] = [
{
- label: "Feedback Trigger",
- url: docsUrl + "user/manual/en/setting-up/feedback/setting-up-feedback",
- },
-];
-
-frappe.help.help_links["List/Feedback Request"] = [
- {
- label: "Feedback Request",
- url: docsUrl + "user/manual/en/setting-up/feedback/submit-feedback",
- },
-];
-
-frappe.help.help_links["List/Feedback Request"] = [
- {
- label: "Feedback Request",
- url: docsUrl + "user/manual/en/setting-up/feedback/submit-feedback",
+ label: "Sales Person in Transactions",
+ url:
+ docsUrl +
+ "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions",
},
];
@@ -1019,7 +972,7 @@
frappe.help.help_links["Form/BOM Update Tool"] = [
{
label: "BOM Update Tool",
- url: docsUrl + "user/manual/en/manufacturing/tools/bom-update-tool",
+ url: docsUrl + "user/manual/en/manufacturing/bom-update-tool",
},
];
@@ -1036,7 +989,7 @@
},
];
-frappe.help.help_links["Form/Custom Field"] = [
+frappe.help.help_links["List/Custom Field"] = [
{
label: "Custom Field",
url: docsUrl + "user/manual/en/customize-erpnext/custom-field",
diff --git a/erpnext/public/js/hierarchy-chart.bundle.js b/erpnext/public/js/hierarchy-chart.bundle.js
new file mode 100644
index 0000000..0270313
--- /dev/null
+++ b/erpnext/public/js/hierarchy-chart.bundle.js
@@ -0,0 +1,3 @@
+import "./hierarchy_chart/hierarchy_chart_desktop.js";
+import "./hierarchy_chart/hierarchy_chart_mobile.js";
+import "./templates/node_card.html";
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
new file mode 100644
index 0000000..7b35819
--- /dev/null
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
@@ -0,0 +1,604 @@
+import html2canvas from 'html2canvas';
+erpnext.HierarchyChart = class {
+ /* Options:
+ - doctype
+ - wrapper: wrapper for the hierarchy view
+ - method:
+ - to get the data for each node
+ - this method should return id, name, title, image, and connections for each node
+ */
+ constructor(doctype, wrapper, method) {
+ this.page = wrapper.page;
+ this.method = method;
+ this.doctype = doctype;
+
+ this.setup_page_style();
+ this.page.main.addClass('frappe-card');
+
+ this.nodes = {};
+ this.setup_node_class();
+ }
+
+ setup_page_style() {
+ this.page.main.css({
+ 'min-height': '300px',
+ 'max-height': '600px',
+ 'overflow': 'auto',
+ 'position': 'relative'
+ });
+ }
+
+ setup_node_class() {
+ let me = this;
+ this.Node = class {
+ constructor({
+ id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line
+ }) {
+ // to setup values passed via constructor
+ $.extend(this, arguments[0]);
+
+ this.expanded = 0;
+
+ me.nodes[this.id] = this;
+ me.make_node_element(this);
+
+ if (!me.all_nodes_expanded) {
+ me.setup_node_click_action(this);
+ }
+
+ me.setup_edit_node_action(this);
+ }
+ };
+ }
+
+ make_node_element(node) {
+ let node_card = frappe.render_template('node_card', {
+ id: node.id,
+ name: node.name,
+ title: node.title,
+ image: node.image,
+ parent: node.parent_id,
+ connections: node.connections,
+ is_mobile: false
+ });
+
+ node.parent.append(node_card);
+ node.$link = $(`[id="${node.id}"]`);
+ }
+
+ show() {
+ this.setup_actions();
+ if ($(`[data-fieldname="company"]`).length) return;
+ let me = this;
+
+ let company = this.page.add_field({
+ fieldtype: 'Link',
+ options: 'Company',
+ fieldname: 'company',
+ placeholder: __('Select Company'),
+ default: frappe.defaults.get_default('company'),
+ only_select: true,
+ reqd: 1,
+ change: () => {
+ me.company = undefined;
+ $('#hierarchy-chart-wrapper').remove();
+
+ if (company.get_value()) {
+ me.company = company.get_value();
+
+ // svg for connectors
+ me.make_svg_markers();
+ me.setup_hierarchy();
+ me.render_root_nodes();
+ me.all_nodes_expanded = false;
+ } else {
+ frappe.throw(__('Please select a company first.'));
+ }
+ }
+ });
+
+ company.refresh();
+ $(`[data-fieldname="company"]`).trigger('change');
+ $(`[data-fieldname="company"] .link-field`).css('z-index', 2);
+ }
+
+ setup_actions() {
+ let me = this;
+ this.page.clear_inner_toolbar();
+ this.page.add_inner_button(__('Export'), function() {
+ me.export_chart();
+ });
+
+ this.page.add_inner_button(__('Expand All'), function() {
+ me.load_children(me.root_node, true);
+ me.all_nodes_expanded = true;
+
+ me.page.remove_inner_button(__('Expand All'));
+ me.page.add_inner_button(__('Collapse All'), function() {
+ me.setup_hierarchy();
+ me.render_root_nodes();
+ me.all_nodes_expanded = false;
+
+ me.page.remove_inner_button(__('Collapse All'));
+ me.setup_actions();
+ });
+ });
+ }
+
+ export_chart() {
+ frappe.dom.freeze(__('Exporting...'));
+ this.page.main.css({
+ 'min-height': '',
+ 'max-height': '',
+ 'overflow': 'visible',
+ 'position': 'fixed',
+ 'left': '0',
+ 'top': '0'
+ });
+
+ $('.node-card').addClass('exported');
+
+ html2canvas(document.querySelector('#hierarchy-chart-wrapper'), {
+ scrollY: -window.scrollY,
+ scrollX: 0
+ }).then(function(canvas) {
+ // Export the canvas to its data URI representation
+ let dataURL = canvas.toDataURL('image/png');
+
+ // download the image
+ let a = document.createElement('a');
+ a.href = dataURL;
+ a.download = 'hierarchy_chart';
+ a.click();
+ }).finally(() => {
+ frappe.dom.unfreeze();
+ });
+
+ this.setup_page_style();
+ $('.node-card').removeClass('exported');
+ }
+
+ setup_hierarchy() {
+ if (this.$hierarchy)
+ this.$hierarchy.remove();
+
+ $(`#connectors`).empty();
+
+ // setup hierarchy
+ this.$hierarchy = $(
+ `<ul class="hierarchy">
+ <li class="root-level level">
+ <ul class="node-children"></ul>
+ </li>
+ </ul>`);
+
+ this.page.main
+ .find('#hierarchy-chart')
+ .empty()
+ .append(this.$hierarchy);
+
+ this.nodes = {};
+ }
+
+ make_svg_markers() {
+ $('#hierarchy-chart-wrapper').remove();
+
+ this.page.main.append(`
+ <div id="hierarchy-chart-wrapper">
+ <svg id="arrows" width="100%" height="100%">
+ <defs>
+ <marker id="arrowhead-active" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="var(--blue-500)">
+ <path d="M 0 0 L 10 5 L 0 10 z"></path>
+ </marker>
+ <marker id="arrowhead-collapsed" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="var(--blue-300)">
+ <path d="M 0 0 L 10 5 L 0 10 z"></path>
+ </marker>
+
+ <marker id="arrowstart-active" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="8" markerHeight="8" orient="auto" fill="var(--blue-500)">
+ <circle cx="4" cy="4" r="3.5" fill="white" stroke="var(--blue-500)"/>
+ </marker>
+ <marker id="arrowstart-collapsed" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="8" markerHeight="8" orient="auto" fill="var(--blue-300)">
+ <circle cx="4" cy="4" r="3.5" fill="white" stroke="var(--blue-300)"/>
+ </marker>
+ </defs>
+ <g id="connectors" fill="none">
+ </g>
+ </svg>
+ <div id="hierarchy-chart">
+ </div>
+ </div>`);
+ }
+
+ render_root_nodes(expanded_view=false) {
+ let me = this;
+
+ return frappe.call({
+ method: me.method,
+ args: {
+ company: me.company
+ }
+ }).then(r => {
+ if (r.message.length) {
+ let expand_node = undefined;
+ let node = undefined;
+
+ $.each(r.message, (_i, data) => {
+ if ($(`[id="${data.id}"]`).length)
+ return;
+
+ node = new me.Node({
+ id: data.id,
+ parent: $('<li class="child-node"></li>').appendTo(me.$hierarchy.find('.node-children')),
+ parent_id: undefined,
+ image: data.image,
+ name: data.name,
+ title: data.title,
+ expandable: true,
+ connections: data.connections,
+ is_root: true
+ });
+
+ if (!expand_node && data.connections)
+ expand_node = node;
+ });
+
+ me.root_node = expand_node;
+ if (!expanded_view) {
+ me.expand_node(expand_node);
+ }
+ }
+ });
+ }
+
+ expand_node(node) {
+ const is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id;
+ this.set_selected_node(node);
+ this.show_active_path(node);
+ this.collapse_previous_level_nodes(node);
+
+ // since the previous node collapses, all connections to that node need to be rebuilt
+ // if a sibling node is clicked, connections don't need to be rebuilt
+ if (!is_sibling) {
+ // rebuild outgoing connections
+ this.refresh_connectors(node.parent_id);
+
+ // rebuild incoming connections
+ let grandparent = $(`[id="${node.parent_id}"]`).attr('data-parent');
+ this.refresh_connectors(grandparent);
+ }
+
+ if (node.expandable && !node.expanded) {
+ return this.load_children(node);
+ }
+ }
+
+ collapse_node() {
+ if (this.selected_node.expandable) {
+ this.selected_node.$children.hide();
+ $(`path[data-parent="${this.selected_node.id}"]`).hide();
+ this.selected_node.expanded = false;
+ }
+ }
+
+ show_active_path(node) {
+ // mark node parent on active path
+ $(`[id="${node.parent_id}"]`).addClass('active-path');
+ }
+
+ load_children(node, deep=false) {
+ if (!deep) {
+ frappe.run_serially([
+ () => this.get_child_nodes(node.id),
+ (child_nodes) => this.render_child_nodes(node, child_nodes)
+ ]);
+ } else {
+ frappe.run_serially([
+ () => frappe.dom.freeze(),
+ () => this.setup_hierarchy(),
+ () => this.render_root_nodes(true),
+ () => this.get_all_nodes(),
+ (data_list) => this.render_children_of_all_nodes(data_list),
+ () => frappe.dom.unfreeze()
+ ]);
+ }
+ }
+
+ get_child_nodes(node_id) {
+ return new Promise(resolve => {
+ frappe.call({
+ method: this.method,
+ args: {
+ parent: node_id,
+ company: this.company
+ }
+ }).then(r => resolve(r.message));
+ });
+ }
+
+ render_child_nodes(node, child_nodes) {
+ const last_level = this.$hierarchy.find('.level:last').index();
+ const current_level = $(`[id="${node.id}"]`).parent().parent().parent().index();
+
+ if (last_level === current_level) {
+ this.$hierarchy.append(`
+ <li class="level"></li>
+ `);
+ }
+
+ if (!node.$children) {
+ node.$children = $('<ul class="node-children"></ul>')
+ .hide()
+ .appendTo(this.$hierarchy.find('.level:last'));
+
+ node.$children.empty();
+
+ if (child_nodes) {
+ $.each(child_nodes, (_i, data) => {
+ this.add_node(node, data);
+ setTimeout(() => {
+ this.add_connector(node.id, data.id);
+ }, 250);
+ });
+ }
+ }
+
+ node.$children.show();
+ $(`path[data-parent="${node.id}"]`).show();
+ node.expanded = true;
+ }
+
+ get_all_nodes() {
+ return new Promise(resolve => {
+ frappe.call({
+ method: 'erpnext.utilities.hierarchy_chart.get_all_nodes',
+ args: {
+ method: this.method,
+ company: this.company
+ },
+ callback: (r) => {
+ resolve(r.message);
+ }
+ });
+ });
+ }
+
+ render_children_of_all_nodes(data_list) {
+ let entry = undefined;
+ let node = undefined;
+
+ while (data_list.length) {
+ // to avoid overlapping connectors
+ entry = data_list.shift();
+ node = this.nodes[entry.parent];
+ if (node) {
+ this.render_child_nodes_for_expanded_view(node, entry.data);
+ } else if (data_list.length) {
+ data_list.push(entry);
+ }
+ }
+ }
+
+ render_child_nodes_for_expanded_view(node, child_nodes) {
+ node.$children = $('<ul class="node-children"></ul>');
+
+ const last_level = this.$hierarchy.find('.level:last').index();
+ const node_level = $(`[id="${node.id}"]`).parent().parent().parent().index();
+
+ if (last_level === node_level) {
+ this.$hierarchy.append(`
+ <li class="level"></li>
+ `);
+ node.$children.appendTo(this.$hierarchy.find('.level:last'));
+ } else {
+ node.$children.appendTo(this.$hierarchy.find('.level:eq(' + (node_level + 1) + ')'));
+ }
+
+ node.$children.hide().empty();
+
+ if (child_nodes) {
+ $.each(child_nodes, (_i, data) => {
+ this.add_node(node, data);
+ setTimeout(() => {
+ this.add_connector(node.id, data.id);
+ }, 250);
+ });
+ }
+
+ node.$children.show();
+ $(`path[data-parent="${node.id}"]`).show();
+ node.expanded = true;
+ }
+
+ add_node(node, data) {
+ return new this.Node({
+ id: data.id,
+ parent: $('<li class="child-node"></li>').appendTo(node.$children),
+ parent_id: node.id,
+ image: data.image,
+ name: data.name,
+ title: data.title,
+ expandable: data.expandable,
+ connections: data.connections,
+ children: undefined
+ });
+ }
+
+ add_connector(parent_id, child_id) {
+ // using pure javascript for better performance
+ const parent_node = document.querySelector(`#${parent_id}`);
+ const child_node = document.querySelector(`#${child_id}`);
+
+ let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+
+ // we need to connect right side of the parent to the left side of the child node
+ const pos_parent_right = {
+ x: parent_node.offsetLeft + parent_node.offsetWidth,
+ y: parent_node.offsetTop + parent_node.offsetHeight / 2
+ };
+ const pos_child_left = {
+ x: child_node.offsetLeft - 5,
+ y: child_node.offsetTop + child_node.offsetHeight / 2
+ };
+
+ const connector = this.get_connector(pos_parent_right, pos_child_left);
+
+ path.setAttribute('d', connector);
+ this.set_path_attributes(path, parent_id, child_id);
+
+ document.getElementById('connectors').appendChild(path);
+ }
+
+ get_connector(pos_parent_right, pos_child_left) {
+ if (pos_parent_right.y === pos_child_left.y) {
+ // don't add arcs if it's a straight line
+ return "M" +
+ (pos_parent_right.x) + "," + (pos_parent_right.y) + " " +
+ "L"+
+ (pos_child_left.x) + "," + (pos_child_left.y);
+ } else {
+ let arc_1 = "";
+ let arc_2 = "";
+ let offset = 0;
+
+ if (pos_parent_right.y > pos_child_left.y) {
+ // if child is above parent on Y axis 1st arc is anticlocwise
+ // second arc is clockwise
+ arc_1 = "a10,10 1 0 0 10,-10 ";
+ arc_2 = "a10,10 0 0 1 10,-10 ";
+ offset = 10;
+ } else {
+ // if child is below parent on Y axis 1st arc is clockwise
+ // second arc is anticlockwise
+ arc_1 = "a10,10 0 0 1 10,10 ";
+ arc_2 = "a10,10 1 0 0 10,10 ";
+ offset = -10;
+ }
+
+ return "M" + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " +
+ "L" +
+ (pos_parent_right.x + 40) + "," + (pos_parent_right.y) + " " +
+ arc_1 +
+ "L" +
+ (pos_parent_right.x + 50) + "," + (pos_child_left.y + offset) + " " +
+ arc_2 +
+ "L"+
+ (pos_child_left.x) + "," + (pos_child_left.y);
+ }
+ }
+
+ set_path_attributes(path, parent_id, child_id) {
+ path.setAttribute("data-parent", parent_id);
+ path.setAttribute("data-child", child_id);
+ const parent = $(`[id="${parent_id}"]`);
+
+ if (parent.hasClass('active')) {
+ path.setAttribute("class", "active-connector");
+ path.setAttribute("marker-start", "url(#arrowstart-active)");
+ path.setAttribute("marker-end", "url(#arrowhead-active)");
+ } else {
+ path.setAttribute("class", "collapsed-connector");
+ path.setAttribute("marker-start", "url(#arrowstart-collapsed)");
+ path.setAttribute("marker-end", "url(#arrowhead-collapsed)");
+ }
+ }
+
+ set_selected_node(node) {
+ // remove active class from the current node
+ if (this.selected_node)
+ this.selected_node.$link.removeClass('active');
+
+ // add active class to the newly selected node
+ this.selected_node = node;
+ node.$link.addClass('active');
+ }
+
+ collapse_previous_level_nodes(node) {
+ let node_parent = $(`[id="${node.parent_id}"]`);
+ let previous_level_nodes = node_parent.parent().parent().children('li');
+ let node_card = undefined;
+
+ previous_level_nodes.each(function() {
+ node_card = $(this).find('.node-card');
+
+ if (!node_card.hasClass('active-path')) {
+ node_card.addClass('collapsed');
+ }
+ });
+ }
+
+ refresh_connectors(node_parent) {
+ if (!node_parent) return;
+
+ $(`path[data-parent="${node_parent}"]`).remove();
+
+ frappe.run_serially([
+ () => this.get_child_nodes(node_parent),
+ (child_nodes) => {
+ if (child_nodes) {
+ $.each(child_nodes, (_i, data) => {
+ this.add_connector(node_parent, data.id);
+ });
+ }
+ }
+ ]);
+ }
+
+ setup_node_click_action(node) {
+ let me = this;
+ let node_element = $(`[id="${node.id}"]`);
+
+ node_element.click(function() {
+ const is_sibling = me.selected_node.parent_id === node.parent_id;
+
+ if (is_sibling) {
+ me.collapse_node();
+ } else if (node_element.is(':visible')
+ && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) {
+ me.remove_levels_after_node(node);
+ me.remove_orphaned_connectors();
+ }
+
+ me.expand_node(node);
+ });
+ }
+
+ setup_edit_node_action(node) {
+ let node_element = $(`[id="${node.id}"]`);
+ let me = this;
+
+ node_element.find('.btn-edit-node').click(function() {
+ frappe.set_route('Form', me.doctype, node.id);
+ });
+ }
+
+ remove_levels_after_node(node) {
+ let level = $(`[id="${node.id}"]`).parent().parent().parent().index();
+
+ level = $('.hierarchy > li:eq('+ level + ')');
+ level.nextAll('li').remove();
+
+ let nodes = level.find('.node-card');
+ let node_object = undefined;
+
+ $.each(nodes, (_i, element) => {
+ node_object = this.nodes[element.id];
+ node_object.expanded = 0;
+ node_object.$children = undefined;
+ });
+
+ nodes.removeClass('collapsed active-path');
+ }
+
+ remove_orphaned_connectors() {
+ let paths = $('#connectors > path');
+ $.each(paths, (_i, path) => {
+ const parent = $(path).data('parent');
+ const child = $(path).data('child');
+
+ if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length)
+ return;
+
+ $(path).remove();
+ });
+ }
+};
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
new file mode 100644
index 0000000..0a8ba78
--- /dev/null
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
@@ -0,0 +1,549 @@
+erpnext.HierarchyChartMobile = class {
+ /* Options:
+ - doctype
+ - wrapper: wrapper for the hierarchy view
+ - method:
+ - to get the data for each node
+ - this method should return id, name, title, image, and connections for each node
+ */
+ constructor(doctype, wrapper, method) {
+ this.page = wrapper.page;
+ this.method = method;
+ this.doctype = doctype;
+
+ this.page.main.css({
+ 'min-height': '300px',
+ 'max-height': '600px',
+ 'overflow': 'auto',
+ 'position': 'relative'
+ });
+ this.page.main.addClass('frappe-card');
+
+ this.nodes = {};
+ this.setup_node_class();
+ }
+
+ setup_node_class() {
+ let me = this;
+ this.Node = class {
+ constructor({
+ id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line
+ }) {
+ // to setup values passed via constructor
+ $.extend(this, arguments[0]);
+
+ this.expanded = 0;
+
+ me.nodes[this.id] = this;
+ me.make_node_element(this);
+ me.setup_node_click_action(this);
+ me.setup_edit_node_action(this);
+ }
+ };
+ }
+
+ make_node_element(node) {
+ let node_card = frappe.render_template('node_card', {
+ id: node.id,
+ name: node.name,
+ title: node.title,
+ image: node.image,
+ parent: node.parent_id,
+ connections: node.connections,
+ is_mobile: true
+ });
+
+ node.parent.append(node_card);
+ node.$link = $(`[id="${node.id}"]`);
+ node.$link.addClass('mobile-node');
+ }
+
+ show() {
+ let me = this;
+ if ($(`[data-fieldname="company"]`).length) return;
+
+ let company = this.page.add_field({
+ fieldtype: 'Link',
+ options: 'Company',
+ fieldname: 'company',
+ placeholder: __('Select Company'),
+ default: frappe.defaults.get_default('company'),
+ only_select: true,
+ reqd: 1,
+ change: () => {
+ me.company = undefined;
+
+ if (company.get_value() && me.company != company.get_value()) {
+ me.company = company.get_value();
+
+ // svg for connectors
+ me.make_svg_markers();
+
+ if (me.$sibling_group)
+ me.$sibling_group.remove();
+
+ // setup sibling group wrapper
+ me.$sibling_group = $(`<div class="sibling-group mt-4 mb-4"></div>`);
+ me.page.main.append(me.$sibling_group);
+
+ me.setup_hierarchy();
+ me.render_root_nodes();
+ }
+ }
+ });
+
+ company.refresh();
+ $(`[data-fieldname="company"]`).trigger('change');
+ }
+
+ make_svg_markers() {
+ $('#arrows').remove();
+
+ this.page.main.prepend(`
+ <svg id="arrows" width="100%" height="100%">
+ <defs>
+ <marker id="arrowhead-active" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="var(--blue-500)">
+ <path d="M 0 0 L 10 5 L 0 10 z"></path>
+ </marker>
+ <marker id="arrowhead-collapsed" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="var(--blue-300)">
+ <path d="M 0 0 L 10 5 L 0 10 z"></path>
+ </marker>
+
+ <marker id="arrowstart-active" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="8" markerHeight="8" orient="auto" fill="var(--blue-500)">
+ <circle cx="4" cy="4" r="3.5" fill="white" stroke="var(--blue-500)"/>
+ </marker>
+ <marker id="arrowstart-collapsed" viewBox="0 0 10 10" refX="3" refY="5" markerWidth="8" markerHeight="8" orient="auto" fill="var(--blue-300)">
+ <circle cx="4" cy="4" r="3.5" fill="white" stroke="var(--blue-300)"/>
+ </marker>
+ </defs>
+ <g id="connectors" fill="none">
+ </g>
+ </svg>`);
+ }
+
+ setup_hierarchy() {
+ $(`#connectors`).empty();
+ if (this.$hierarchy)
+ this.$hierarchy.remove();
+
+ if (this.$sibling_group)
+ this.$sibling_group.empty();
+
+ this.$hierarchy = $(
+ `<ul class="hierarchy-mobile">
+ <li class="root-level level"></li>
+ </ul>`);
+
+ this.page.main.append(this.$hierarchy);
+ }
+
+ render_root_nodes() {
+ let me = this;
+
+ frappe.call({
+ method: me.method,
+ args: {
+ company: me.company
+ },
+ }).then(r => {
+ if (r.message.length) {
+ let root_level = me.$hierarchy.find('.root-level');
+ root_level.empty();
+
+ $.each(r.message, (_i, data) => {
+ return new me.Node({
+ id: data.id,
+ parent: root_level,
+ parent_id: undefined,
+ image: data.image,
+ name: data.name,
+ title: data.title,
+ expandable: true,
+ connections: data.connections,
+ is_root: true
+ });
+ });
+ }
+ });
+ }
+
+ expand_node(node) {
+ const is_same_node = (this.selected_node && this.selected_node.id === node.id);
+ this.set_selected_node(node);
+ this.show_active_path(node);
+
+ if (this.$sibling_group) {
+ const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent');
+ if (node.parent_id !== undefined && node.parent_id != sibling_parent)
+ this.$sibling_group.empty();
+ }
+
+ if (!is_same_node) {
+ // since the previous/parent node collapses, all connections to that node need to be rebuilt
+ // rebuild outgoing connections of parent
+ this.refresh_connectors(node.parent_id, node.id);
+
+ // rebuild incoming connections of parent
+ let grandparent = $(`[id="${node.parent_id}"]`).attr('data-parent');
+ this.refresh_connectors(grandparent, node.parent_id);
+ }
+
+ if (node.expandable && !node.expanded) {
+ return this.load_children(node);
+ }
+ }
+
+ collapse_node() {
+ let node = this.selected_node;
+ if (node.expandable && node.$children) {
+ node.$children.hide();
+ node.expanded = 0;
+
+ // add a collapsed level to show the collapsed parent
+ // and a button beside it to move to that level
+ let node_parent = node.$link.parent();
+ node_parent.prepend(
+ `<div class="collapsed-level d-flex flex-row"></div>`
+ );
+
+ node_parent
+ .find('.collapsed-level')
+ .append(node.$link);
+
+ frappe.run_serially([
+ () => this.get_child_nodes(node.parent_id, node.id),
+ (child_nodes) => this.get_node_group(child_nodes, node.parent_id),
+ (node_group) => node_parent.find('.collapsed-level').append(node_group),
+ () => this.setup_node_group_action()
+ ]);
+ }
+ }
+
+ show_active_path(node) {
+ // mark node parent on active path
+ $(`[id="${node.parent_id}"]`).addClass('active-path');
+ }
+
+ load_children(node) {
+ frappe.run_serially([
+ () => this.get_child_nodes(node.id),
+ (child_nodes) => this.render_child_nodes(node, child_nodes)
+ ]);
+ }
+
+ get_child_nodes(node_id, exclude_node=null) {
+ let me = this;
+ return new Promise(resolve => {
+ frappe.call({
+ method: this.method,
+ args: {
+ parent: node_id,
+ company: me.company,
+ exclude_node: exclude_node
+ }
+ }).then(r => resolve(r.message));
+ });
+ }
+
+ render_child_nodes(node, child_nodes) {
+ if (!node.$children) {
+ node.$children = $('<ul class="node-children"></ul>')
+ .hide()
+ .appendTo(node.$link.parent());
+
+ node.$children.empty();
+
+ if (child_nodes) {
+ $.each(child_nodes, (_i, data) => {
+ this.add_node(node, data);
+ $(`[id="${data.id}"]`).addClass('active-child');
+
+ setTimeout(() => {
+ this.add_connector(node.id, data.id);
+ }, 250);
+ });
+ }
+ }
+
+ node.$children.show();
+ node.expanded = 1;
+ }
+
+ add_node(node, data) {
+ var $li = $('<li class="child-node"></li>');
+
+ return new this.Node({
+ id: data.id,
+ parent: $li.appendTo(node.$children),
+ parent_id: node.id,
+ image: data.image,
+ name: data.name,
+ title: data.title,
+ expandable: data.expandable,
+ connections: data.connections,
+ children: undefined
+ });
+ }
+
+ add_connector(parent_id, child_id) {
+ const parent_node = document.querySelector(`#${parent_id}`);
+ const child_node = document.querySelector(`#${child_id}`);
+
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+
+ let connector = undefined;
+
+ if ($(`[id="${parent_id}"]`).hasClass('active')) {
+ connector = this.get_connector_for_active_node(parent_node, child_node);
+ } else if ($(`[id="${parent_id}"]`).hasClass('active-path')) {
+ connector = this.get_connector_for_collapsed_node(parent_node, child_node);
+ }
+
+ path.setAttribute('d', connector);
+ this.set_path_attributes(path, parent_id, child_id);
+
+ document.getElementById('connectors').appendChild(path);
+ }
+
+ get_connector_for_active_node(parent_node, child_node) {
+ // we need to connect the bottom left of the parent to the left side of the child node
+ let pos_parent_bottom = {
+ x: parent_node.offsetLeft + 20,
+ y: parent_node.offsetTop + parent_node.offsetHeight
+ };
+ let pos_child_left = {
+ x: child_node.offsetLeft - 5,
+ y: child_node.offsetTop + child_node.offsetHeight / 2
+ };
+
+ let connector =
+ "M" +
+ (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " +
+ "L" +
+ (pos_parent_bottom.x) + "," + (pos_child_left.y - 10) + " " +
+ "a10,10 1 0 0 10,10 " +
+ "L" +
+ (pos_child_left.x) + "," + (pos_child_left.y);
+
+ return connector;
+ }
+
+ get_connector_for_collapsed_node(parent_node, child_node) {
+ // we need to connect the bottom left of the parent to the top left of the child node
+ let pos_parent_bottom = {
+ x: parent_node.offsetLeft + 20,
+ y: parent_node.offsetTop + parent_node.offsetHeight
+ };
+ let pos_child_top = {
+ x: child_node.offsetLeft + 20,
+ y: child_node.offsetTop
+ };
+
+ let connector =
+ "M" +
+ (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " +
+ "L" +
+ (pos_child_top.x) + "," + (pos_child_top.y);
+
+ return connector;
+ }
+
+ set_path_attributes(path, parent_id, child_id) {
+ path.setAttribute("data-parent", parent_id);
+ path.setAttribute("data-child", child_id);
+ const parent = $(`[id="${parent_id}"]`);
+
+ if (parent.hasClass('active')) {
+ path.setAttribute("class", "active-connector");
+ path.setAttribute("marker-start", "url(#arrowstart-active)");
+ path.setAttribute("marker-end", "url(#arrowhead-active)");
+ } else if (parent.hasClass('active-path')) {
+ path.setAttribute("class", "collapsed-connector");
+ }
+ }
+
+ set_selected_node(node) {
+ // remove .active class from the current node
+ if (this.selected_node)
+ this.selected_node.$link.removeClass('active');
+
+ // add active class to the newly selected node
+ this.selected_node = node;
+ node.$link.addClass('active');
+ }
+
+ setup_node_click_action(node) {
+ let me = this;
+ let node_element = $(`[id="${node.id}"]`);
+
+ node_element.click(function() {
+ let el = undefined;
+
+ if (node.is_root) {
+ el = $(this).detach();
+ me.$hierarchy.empty();
+ $(`#connectors`).empty();
+ me.add_node_to_hierarchy(el, node);
+ } else if (node_element.is(':visible') && node_element.hasClass('active-path')) {
+ me.remove_levels_after_node(node);
+ me.remove_orphaned_connectors();
+ } else {
+ el = $(this).detach();
+ me.add_node_to_hierarchy(el, node);
+ me.collapse_node();
+ }
+
+ me.expand_node(node);
+ });
+ }
+
+ setup_edit_node_action(node) {
+ let node_element = $(`[id="${node.id}"]`);
+ let me = this;
+
+ node_element.find('.btn-edit-node').click(function() {
+ frappe.set_route('Form', me.doctype, node.id);
+ });
+ }
+
+ setup_node_group_action() {
+ let me = this;
+
+ $('.node-group').on('click', function() {
+ let parent = $(this).attr('data-parent');
+ if (parent === 'undefined') {
+ me.setup_hierarchy();
+ me.render_root_nodes();
+ } else {
+ me.expand_sibling_group_node(parent);
+ }
+ });
+ }
+
+ add_node_to_hierarchy(node_element, node) {
+ this.$hierarchy.append(`<li class="level"></li>`);
+ node_element.removeClass('active-child active-path');
+ this.$hierarchy.find('.level:last').append(node_element);
+
+ let node_object = this.nodes[node.id];
+ node_object.expanded = 0;
+ node_object.$children = undefined;
+ this.nodes[node.id] = node_object;
+ }
+
+ get_node_group(nodes, parent, collapsed=true) {
+ let limit = 2;
+ const display_nodes = nodes.slice(0, limit);
+ const extra_nodes = nodes.slice(limit);
+
+ let html = display_nodes.map(node =>
+ this.get_avatar(node)
+ ).join('');
+
+ if (extra_nodes.length === 1) {
+ let node = extra_nodes[0];
+ html += this.get_avatar(node);
+ } else if (extra_nodes.length > 1) {
+ html = `
+ ${html}
+ <span class="avatar avatar-small">
+ <div class="avatar-frame standard-image avatar-extra-count"
+ title="${extra_nodes.map(node => node.name).join(', ')}">
+ +${extra_nodes.length}
+ </div>
+ </span>
+ `;
+ }
+
+ if (html) {
+ const $node_group =
+ $(`<div class="node-group card cursor-pointer" data-parent=${parent}>
+ <div class="avatar-group right overlap">
+ ${html}
+ </div>
+ </div>`);
+
+ if (collapsed)
+ $node_group.addClass('collapsed');
+
+ return $node_group;
+ }
+
+ return null;
+ }
+
+ get_avatar(node) {
+ return `<span class="avatar avatar-small" title="${node.name}">
+ <span class="avatar-frame" src=${node.image} style="background-image: url(${node.image})"></span>
+ </span>`;
+ }
+
+ expand_sibling_group_node(parent) {
+ let node_object = this.nodes[parent];
+ let node = node_object.$link;
+
+ node.removeClass('active-child active-path');
+ node_object.expanded = 0;
+ node_object.$children = undefined;
+ this.nodes[node.id] = node_object;
+
+ // show parent's siblings and expand parent node
+ frappe.run_serially([
+ () => this.get_child_nodes(node_object.parent_id, node_object.id),
+ (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false),
+ (node_group) => {
+ if (node_group)
+ this.$sibling_group.empty().append(node_group);
+ },
+ () => this.setup_node_group_action(),
+ () => this.reattach_and_expand_node(node, node_object)
+ ]);
+ }
+
+ reattach_and_expand_node(node, node_object) {
+ var el = node.detach();
+
+ this.$hierarchy.empty().append(`
+ <li class="level"></li>
+ `);
+ this.$hierarchy.find('.level').append(el);
+ $(`#connectors`).empty();
+ this.expand_node(node_object);
+ }
+
+ remove_levels_after_node(node) {
+ let level = $(`[id="${node.id}"]`).parent().parent().index();
+
+ level = $('.hierarchy-mobile > li:eq('+ level + ')');
+ level.nextAll('li').remove();
+
+ let node_object = this.nodes[node.id];
+ let current_node = level.find(`#${node.id}`).detach();
+ current_node.removeClass('active-child active-path');
+
+ node_object.expanded = 0;
+ node_object.$children = undefined;
+
+ level.empty().append(current_node);
+ }
+
+ remove_orphaned_connectors() {
+ let paths = $('#connectors > path');
+ $.each(paths, (_i, path) => {
+ const parent = $(path).data('parent');
+ const child = $(path).data('child');
+
+ if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length)
+ return;
+
+ $(path).remove();
+ });
+ }
+
+ refresh_connectors(node_parent, node_id) {
+ if (!node_parent) return;
+
+ $(`path[data-parent="${node_parent}"]`).remove();
+ this.add_connector(node_parent, node_id);
+ }
+};
diff --git a/erpnext/public/js/hub/components/ReviewArea.vue b/erpnext/public/js/hub/components/ReviewArea.vue
index 5e4e439..aa83bb0 100644
--- a/erpnext/public/js/hub/components/ReviewArea.vue
+++ b/erpnext/public/js/hub/components/ReviewArea.vue
@@ -137,4 +137,4 @@
}
}
}
-</script>
\ No newline at end of file
+</script>
diff --git a/erpnext/public/js/hub/components/ReviewTimelineItem.vue b/erpnext/public/js/hub/components/ReviewTimelineItem.vue
index f0fe001..d0e83f3 100644
--- a/erpnext/public/js/hub/components/ReviewTimelineItem.vue
+++ b/erpnext/public/js/hub/components/ReviewTimelineItem.vue
@@ -51,4 +51,3 @@
}
}
</script>
-
diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue
index 63ae7e9..8380b2b 100644
--- a/erpnext/public/js/hub/pages/FeaturedItems.vue
+++ b/erpnext/public/js/hub/pages/FeaturedItems.vue
@@ -69,7 +69,7 @@
const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
- alert_message = __('{0} removed. {1}', [item_name,
+ alert_message = __('{0} removed. {1}', [item_name,
`<a href="#" data-action="undo-remove"><b>${__('Undo')}</b></a>`]);
alert = frappe.show_alert(alert_message, grace_period / 1000,
{
diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue
index 96fa0aa..ecba4b1 100644
--- a/erpnext/public/js/hub/pages/Publish.vue
+++ b/erpnext/public/js/hub/pages/Publish.vue
@@ -78,7 +78,7 @@
empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'),
valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'),
last_sync_message: (hub.settings.last_sync_datetime)
- ? __('Last sync was {0}.', [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]) +
+ ? __('Last sync was {0}.', [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]) +
` <a href="#marketplace/published-items">${__('See your Published Items.')}</a>`
: ''
};
diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue
index c0903c6..3c9b800 100644
--- a/erpnext/public/js/hub/pages/Seller.vue
+++ b/erpnext/public/js/hub/pages/Seller.vue
@@ -24,7 +24,7 @@
<div v-if="items.length">
<h5>
- {{ item_container_heading }}
+ {{ item_container_heading }}
<small v-if="is_user_registered() && is_own_company">
<a class="pull-right" href="#marketplace/featured-items">Customize your Featured Items</a>
</small>
@@ -160,7 +160,7 @@
];
setTimeout(() => this.init_seller_traffic_chart(), 1);
-
+
});
},
diff --git a/erpnext/public/js/hub/vue-plugins.js b/erpnext/public/js/hub/vue-plugins.js
index 6e6a7cb..4912d68 100644
--- a/erpnext/public/js/hub/vue-plugins.js
+++ b/erpnext/public/js/hub/vue-plugins.js
@@ -55,4 +55,4 @@
Vue.filter('striphtml', function (text) {
return strip_html(text || '');
-});
\ No newline at end of file
+});
diff --git a/erpnext/public/js/leaflet/leaflet.draw.js b/erpnext/public/js/leaflet/leaflet.draw.js
index 4352f70..26f1e19 100755
--- a/erpnext/public/js/leaflet/leaflet.draw.js
+++ b/erpnext/public/js/leaflet/leaflet.draw.js
@@ -140,4 +140,4 @@
e.on("click", this._removeLayer, this) }, _disableLayerDelete: function(t) { var e = t.layer || t.target || t;
e.off("click", this._removeLayer, this), this._deletedLayers.removeLayer(e) }, _removeLayer: function(t) { var e = t.layer || t.target || t;
this._deletableLayers.removeLayer(e), this._deletedLayers.addLayer(e) }, _onMouseMove: function(t) { this._tooltip.updatePosition(t.latlng) }, _hasAvailableLayers: function() { return 0 !== this._deletableLayers.getLayers().length } })
-}(window, document);
\ No newline at end of file
+}(window, document);
diff --git a/erpnext/public/js/leaflet/leaflet.js b/erpnext/public/js/leaflet/leaflet.js
index 41d9bb9..91dd3d4 100755
--- a/erpnext/public/js/leaflet/leaflet.js
+++ b/erpnext/public/js/leaflet/leaflet.js
@@ -768,4 +768,4 @@
r = this._locateOptions; if (r.setView) { var a = this.getBoundsZoom(s);
this.setView(n, r.maxZoom ? Math.min(a, r.maxZoom) : a) } var h = { latlng: n, bounds: s, timestamp: t.timestamp }; for (var l in t.coords) "number" == typeof t.coords[l] && (h[l] = t.coords[l]);
this.fire("locationfound", h) } })
-}(window, document);
\ No newline at end of file
+}(window, document);
diff --git a/erpnext/public/js/projects/timer.js b/erpnext/public/js/projects/timer.js
index 26be997..0e5c0d3 100644
--- a/erpnext/public/js/projects/timer.js
+++ b/erpnext/public/js/projects/timer.js
@@ -159,4 +159,4 @@
$btn_complete.hide();
$btn_start.show();
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index 6f5d67c..38e1eb5 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -147,7 +147,7 @@
}
// Validate bank name
- if(me.values.bank_account) {
+ if(me.values.bank_account) {
frappe.call({
async: false,
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.validate_bank_account",
diff --git a/erpnext/public/js/stock_analytics.js b/erpnext/public/js/stock_analytics.js
index dfe2c88..a343c34 100644
--- a/erpnext/public/js/stock_analytics.js
+++ b/erpnext/public/js/stock_analytics.js
@@ -204,4 +204,3 @@
frappe.set_route("query-report", "Stock Ledger");
}
};
-
diff --git a/erpnext/public/js/templates/item_quick_entry.html b/erpnext/public/js/templates/item_quick_entry.html
index 6a5f36d..e5e7869 100644
--- a/erpnext/public/js/templates/item_quick_entry.html
+++ b/erpnext/public/js/templates/item_quick_entry.html
@@ -1,3 +1,3 @@
<div class="h6 uppercase" style="margin-top: 30px;">{{ __("Variant Attributes") }}</div>
<div class="attributes hide-control">
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/public/js/templates/item_selector.html b/erpnext/public/js/templates/item_selector.html
index 58fb26c..86a15f4 100644
--- a/erpnext/public/js/templates/item_selector.html
+++ b/erpnext/public/js/templates/item_selector.html
@@ -34,4 +34,4 @@
</div>
{% if ((i % 4 === 3) || (i===data.length - 1)) { %}</div>{% } %}
{% endfor %}
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html
new file mode 100644
index 0000000..4cb6ee0
--- /dev/null
+++ b/erpnext/public/js/templates/node_card.html
@@ -0,0 +1,33 @@
+<div class="node-card card cursor-pointer" id="{%= id %}" data-parent="{%= parent %}">
+ <div class="node-meta d-flex flex-row">
+ <div class="mr-3">
+ <span class="avatar node-image" title="{{ name }}">
+ <span class="avatar-frame" src={{image}} style="background-image: url(\'{%= image %}\')"></span>
+ </span>
+ </div>
+ <div>
+ <div class="node-name d-flex flex-row mb-1">
+ <span class="ellipsis">{{ name }}</span>
+ <div class="btn-xs btn-edit-node d-flex flex-row">
+ <a class="node-edit-icon">{{ frappe.utils.icon("edit", "xs") }}</a>
+ <span class="edit-chart-node text-xs">{{ __("Edit") }}</span>
+ </div>
+ </div>
+ <div class="node-info d-flex flex-row mb-1">
+ <div class="node-title text-muted ellipsis">{{ title }}</div>
+
+ {% if is_mobile %}
+ <div class="node-connections text-muted ml-2 ellipsis">
+ · {{ connections }} <span class="fa fa-level-down"></span>
+ </div>
+ {% else %}
+ {% if connections == 1 %}
+ <div class="node-connections text-muted ml-2 ellipsis">· {{ connections }} Connection</div>
+ {% else %}
+ <div class="node-connections text-muted ml-2 ellipsis">· {{ connections }} Connections</div>
+ {% endif %}
+ {% endif %}
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index db7c034..0323a42 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -82,6 +82,17 @@
});
frappe.set_route('Form','Journal Entry', journal_entry.name);
});
+ },
+
+ proceed_save_with_reminders_frequency_change: () => {
+ frappe.ui.hide_open_dialog();
+
+ frappe.call({
+ method: 'erpnext.hr.doctype.hr_settings.hr_settings.set_proceed_with_frequency_change',
+ callback: () => {
+ cur_frm.save();
+ }
+ });
}
});
@@ -465,7 +476,23 @@
in_list_view: 1,
read_only: 0,
disabled: 0,
- label: __('Item Code')
+ label: __('Item Code'),
+ get_query: function() {
+ let filters;
+ if (frm.doc.doctype == 'Sales Order') {
+ filters = {"is_sales_item": 1};
+ } else if (frm.doc.doctype == 'Purchase Order') {
+ if (frm.doc.is_subcontracted == "Yes") {
+ filters = {"is_sub_contracted_item": 1};
+ } else {
+ filters = {"is_purchase_item": 1};
+ }
+ }
+ return {
+ query: "erpnext.controllers.queries.item_query",
+ filters: filters
+ };
+ }
}, {
fieldtype:'Link',
fieldname:'uom',
@@ -547,7 +574,7 @@
},
],
primary_action: function() {
- const trans_items = this.get_values()["trans_items"];
+ const trans_items = this.get_values()["trans_items"].filter((item) => !!item.item_code);
frappe.call({
method: 'erpnext.controllers.accounts_controller.update_child_qty_rate',
freeze: true,
@@ -682,14 +709,21 @@
setters: opts.setters,
get_query: opts.get_query,
add_filters_group: 1,
+ allow_child_item_selection: opts.allow_child_item_selection,
+ child_fieldname: opts.child_fielname,
+ child_columns: opts.child_columns,
+ size: opts.size,
action: function(selections, args) {
let values = selections;
- if(values.length === 0){
+ if (values.length === 0) {
frappe.msgprint(__("Please select {0}", [opts.source_doctype]))
return;
}
opts.source_name = values;
- opts.setters = args;
+ if (opts.allow_child_item_selection) {
+ // args contains filtered child docnames
+ opts.args = args;
+ }
d.dialog.hide();
_map();
},
diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js
index 96e1817..bb23f15 100644
--- a/erpnext/public/js/utils/dimension_tree_filter.js
+++ b/erpnext/public/js/utils/dimension_tree_filter.js
@@ -100,4 +100,4 @@
});
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 4d432e3..a492b32 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -289,8 +289,8 @@
company: frm.doc.company,
address: frm.doc.shipping_address
},
- callback: function(r){
- if (r.message){
+ callback: function(r) {
+ if (r.message) {
frm.set_value("shipping_address", r.message[0]) //Address title or name
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
}
diff --git a/erpnext/public/js/utils/supplier_quick_entry.js b/erpnext/public/js/utils/supplier_quick_entry.js
new file mode 100644
index 0000000..8d591a9
--- /dev/null
+++ b/erpnext/public/js/utils/supplier_quick_entry.js
@@ -0,0 +1,77 @@
+frappe.provide('frappe.ui.form');
+
+frappe.ui.form.SupplierQuickEntryForm = class SupplierQuickEntryForm extends frappe.ui.form.QuickEntryForm {
+ constructor(doctype, after_insert, init_callback, doc, force) {
+ super(doctype, after_insert, init_callback, doc, force);
+ this.skip_redirect_on_error = true;
+ }
+
+ render_dialog() {
+ this.mandatory = this.mandatory.concat(this.get_variant_fields());
+ super.render_dialog();
+ }
+
+ get_variant_fields() {
+ var variant_fields = [
+ {
+ fieldtype: "Section Break",
+ label: __("Primary Contact Details"),
+ collapsible: 1
+ },
+ {
+ label: __("Email Id"),
+ fieldname: "email_id",
+ fieldtype: "Data"
+ },
+ {
+ fieldtype: "Column Break"
+ },
+ {
+ label: __("Mobile Number"),
+ fieldname: "mobile_no",
+ fieldtype: "Data"
+ },
+ {
+ fieldtype: "Section Break",
+ label: __("Primary Address Details"),
+ collapsible: 1
+ },
+ {
+ label: __("Address Line 1"),
+ fieldname: "address_line1",
+ fieldtype: "Data"
+ },
+ {
+ label: __("Address Line 2"),
+ fieldname: "address_line2",
+ fieldtype: "Data"
+ },
+ {
+ label: __("ZIP Code"),
+ fieldname: "pincode",
+ fieldtype: "Data"
+ },
+ {
+ fieldtype: "Column Break"
+ },
+ {
+ label: __("City"),
+ fieldname: "city",
+ fieldtype: "Data"
+ },
+ {
+ label: __("State"),
+ fieldname: "state",
+ fieldtype: "Data"
+ },
+ {
+ label: __("Country"),
+ fieldname: "country",
+ fieldtype: "Link",
+ options: "Country"
+ }
+ ];
+
+ return variant_fields;
+ }
+};
diff --git a/erpnext/public/scss/erpnext.bundle.scss b/erpnext/public/scss/erpnext.bundle.scss
index d3313c7..b68ddf5 100644
--- a/erpnext/public/scss/erpnext.bundle.scss
+++ b/erpnext/public/scss/erpnext.bundle.scss
@@ -1,3 +1,4 @@
@import "./erpnext";
@import "./call_popup";
@import "./point-of-sale";
+@import "./hierarchy_chart";
diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss
new file mode 100644
index 0000000..57d5e84
--- /dev/null
+++ b/erpnext/public/scss/hierarchy_chart.scss
@@ -0,0 +1,313 @@
+.node-card {
+ background: white;
+ stroke: 1px solid var(--gray-200);
+ box-shadow: var(--shadow-base);
+ border-radius: 0.5rem;
+ padding: 0.75rem;
+ margin-left: 3rem;
+ width: 18rem;
+ overflow: hidden;
+
+ .btn-edit-node {
+ display: none;
+ }
+
+ .edit-chart-node {
+ display: none;
+ }
+
+ .node-edit-icon {
+ display: none;
+ }
+}
+
+.node-card.exported {
+ box-shadow: none
+}
+
+.node-image {
+ width: 3.0rem;
+ height: 3.0rem;
+}
+
+.node-name {
+ font-size: 1rem;
+ line-height: 1.72;
+}
+
+.node-title {
+ font-size: 0.75rem;
+ line-height: 1.35;
+}
+
+.node-info {
+ width: 12.7rem;
+}
+
+.node-connections {
+ font-size: 0.75rem;
+ line-height: 1.35;
+}
+
+.node-card.active {
+ background: var(--blue-50);
+ border: 1px solid var(--blue-500);
+ box-shadow: var(--shadow-md);
+ border-radius: 0.5rem;
+ padding: 0.75rem;
+ width: 18rem;
+
+ .btn-edit-node {
+ display: flex;
+ background: var(--blue-100);
+ color: var(--blue-500);
+ padding: .25rem .5rem;
+ font-size: .75rem;
+ justify-content: center;
+ box-shadow: var(--shadow-sm);
+ margin-left: auto;
+ }
+
+ .edit-chart-node {
+ display: block;
+ margin-right: 0.25rem;
+ }
+
+ .node-edit-icon {
+ display: block;
+ }
+
+ .node-edit-icon > .icon{
+ stroke: var(--blue-500);
+ }
+
+ .node-name {
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 2px;
+ width: 12.2rem;
+ }
+}
+
+.node-card.active-path {
+ background: var(--blue-100);
+ border: 1px solid var(--blue-300);
+ box-shadow: var(--shadow-sm);
+ border-radius: 0.5rem;
+ padding: 0.75rem;
+ width: 15rem;
+ height: 3.0rem;
+
+ .btn-edit-node {
+ display: none !important;
+ }
+
+ .edit-chart-node {
+ display: none;
+ }
+
+ .node-edit-icon {
+ display: none;
+ }
+
+ .node-info {
+ display: none;
+ }
+
+ .node-title {
+ display: none;
+ }
+
+ .node-connections {
+ display: none;
+ }
+
+ .node-name {
+ font-size: 0.85rem;
+ line-height: 1.35;
+ }
+
+ .node-image {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
+
+ .node-meta {
+ align-items: baseline;
+ }
+}
+
+.node-card.collapsed {
+ background: white;
+ stroke: 1px solid var(--gray-200);
+ box-shadow: var(--shadow-sm);
+ border-radius: 0.5rem;
+ padding: 0.75rem;
+ width: 15rem;
+ height: 3.0rem;
+
+ .btn-edit-node {
+ display: none !important;
+ }
+
+ .edit-chart-node {
+ display: none;
+ }
+
+ .node-edit-icon {
+ display: none;
+ }
+
+ .node-info {
+ display: none;
+ }
+
+ .node-title {
+ display: none;
+ }
+
+ .node-connections {
+ display: none;
+ }
+
+ .node-name {
+ font-size: 0.85rem;
+ line-height: 1.35;
+ }
+
+ .node-image {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
+
+ .node-meta {
+ align-items: baseline;
+ }
+}
+
+// horizontal hierarchy tree view
+#hierarchy-chart-wrapper {
+ padding-top: 30px;
+
+ #arrows {
+ margin-top: -80px;
+ }
+}
+
+.hierarchy {
+ display: flex;
+}
+
+.hierarchy li {
+ list-style-type: none;
+}
+
+.child-node {
+ margin: 0px 0px 16px 0px;
+}
+
+.hierarchy, .hierarchy-mobile {
+ .level {
+ margin-right: 8px;
+ align-items: flex-start;
+ flex-direction: column;
+ }
+}
+
+#arrows {
+ position: absolute;
+ overflow: visible;
+}
+
+.active-connector {
+ stroke: var(--blue-500);
+}
+
+.collapsed-connector {
+ stroke: var(--blue-300);
+}
+
+// mobile
+
+.hierarchy-mobile {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-top: 10px;
+ padding-left: 0px;
+}
+
+.hierarchy-mobile li {
+ list-style-type: none;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+}
+
+.mobile-node {
+ margin-left: 0;
+}
+
+.mobile-node.active-path {
+ width: 12.25rem;
+}
+
+.active-child {
+ width: 15.5rem;
+}
+
+.mobile-node .node-connections {
+ max-width: 80px;
+}
+
+.hierarchy-mobile .node-children {
+ margin-top: 16px;
+}
+
+.root-level .node-card {
+ margin: 0 0 16px;
+}
+
+// node group
+
+.collapsed-level {
+ margin-bottom: 16px;
+ width: 18rem;
+}
+
+.node-group {
+ background: white;
+ border: 1px solid var(--gray-300);
+ box-shadow: var(--shadow-sm);
+ border-radius: 0.5rem;
+ padding: 0.75rem;
+ width: 18rem;
+ height: 3rem;
+ overflow: hidden;
+ align-items: center;
+}
+
+.node-group .avatar-group {
+ margin-left: 0px;
+}
+
+.node-group .avatar-extra-count {
+ background-color: var(--blue-100);
+ color: var(--blue-500);
+}
+
+.node-group .avatar-frame {
+ width: 1.5rem;
+ height: 1.5rem;
+}
+
+.node-group.collapsed {
+ width: 5rem;
+ margin-left: 12px;
+}
+
+.sibling-group {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss
index c77b2ce..1677e9b 100644
--- a/erpnext/public/scss/point-of-sale.scss
+++ b/erpnext/public/scss/point-of-sale.scss
@@ -860,6 +860,8 @@
.invoice-fields {
overflow-y: scroll;
+ height: 100%;
+ padding-right: var(--padding-sm);
}
}
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
index 5962859..fef1e76 100644
--- a/erpnext/public/scss/shopping_cart.scss
+++ b/erpnext/public/scss/shopping_cart.scss
@@ -31,6 +31,14 @@
.carousel-control-prev,
.carousel-control-next {
opacity: 1;
+ width: 8%;
+
+ @media (max-width: 1200px) {
+ width: 10%;
+ }
+ @media (max-width: 768px) {
+ width: 15%;
+ }
}
.carousel-body {
@@ -43,6 +51,8 @@
.carousel-content {
max-width: 400px;
+ margin-left: 5rem;
+ margin-right: 5rem;
}
.card {
@@ -483,4 +493,3 @@
border: 1px solid var(--dark-border-color);
}
}
-
diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss
index f4325c0..9ea8416 100644
--- a/erpnext/public/scss/website.scss
+++ b/erpnext/public/scss/website.scss
@@ -67,4 +67,4 @@
.card-body > .card-title {
line-height: 1.3;
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.py b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
index d4e8cc7..a4613fd 100644
--- a/erpnext/quality_management/doctype/non_conformance/non_conformance.py
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class NonConformance(Document):
pass
diff --git a/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
index 54f8b58..759b117 100644
--- a/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
+++ b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestNonConformance(unittest.TestCase):
pass
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.js b/erpnext/quality_management/doctype/quality_action/quality_action.js
index e216a75..b44f2a2 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.js
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.js
@@ -3,4 +3,4 @@
frappe.ui.form.on('Quality Action', {
-});
\ No newline at end of file
+});
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py
index d6fa505..646a0df 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.py
@@ -3,9 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class QualityAction(Document):
def validate(self):
- self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed'
\ No newline at end of file
+ self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed'
diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.py b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
index 24b97ca..33229d4 100644
--- a/erpnext/quality_management/doctype/quality_action/test_quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
@@ -3,9 +3,9 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQualityAction(unittest.TestCase):
# quality action has no code
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.py b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.py
index de8873f..b456fb7 100644
--- a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.py
+++ b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityActionResolution(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
index 5a8ec73..9189c28 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class QualityFeedback(Document):
@frappe.whitelist()
def set_parameters(self):
@@ -21,4 +23,3 @@
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 5a8bd5c..7a87c36 100644
--- a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
class TestQualityFeedback(unittest.TestCase):
def test_quality_feedback(self):
diff --git a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.py b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.py
index d652e8a..9a21b26 100644
--- a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.py
+++ b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityFeedbackParameter(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.py b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.py
index 0c6dfc0..c6a520a 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.py
+++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityFeedbackTemplate(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
index b3eed10..1de58aa 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
+++ b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQualityFeedbackTemplate(unittest.TestCase):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/quality_management/doctype/quality_feedback_template_parameter/quality_feedback_template_parameter.py b/erpnext/quality_management/doctype/quality_feedback_template_parameter/quality_feedback_template_parameter.py
index 3f3348f..44a6b01 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template_parameter/quality_feedback_template_parameter.py
+++ b/erpnext/quality_management/doctype/quality_feedback_template_parameter/quality_feedback_template_parameter.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityFeedbackTemplateParameter(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.py b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
index f3fe986..2888401 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
@@ -3,10 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class QualityGoal(Document):
def validate(self):
- pass
\ No newline at end of file
+ pass
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 f61d6e5..84240d2 100644
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
+
+import frappe
+
class TestQualityGoal(unittest.TestCase):
def test_quality_goal(self):
@@ -22,4 +23,4 @@
objectives = [
dict(objective = 'Check test cases', target='100', uom='Percent')
]
- )).insert()
\ No newline at end of file
+ )).insert()
diff --git a/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.py b/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.py
index f4bd357..c9c2c6e 100644
--- a/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.py
+++ b/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityGoalObjective(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py
index f8de229..0ac0484 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class QualityMeeting(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js
index ff85c84..5fd1b30 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js
@@ -8,4 +8,4 @@
return [__("Close"), "green", ",status=,Close"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
index 754bccb..e57256d 100644
--- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
+++ b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
@@ -3,9 +3,9 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestQualityMeeting(unittest.TestCase):
# nothing to test
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.py b/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.py
index 5d77975..5e4d9ff 100644
--- a/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.py
+++ b/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityMeetingAgenda(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_meeting_agenda/test_quality_meeting_agenda.py b/erpnext/quality_management/doctype/quality_meeting_agenda/test_quality_meeting_agenda.py
index 4750cc1..8744d27 100644
--- a/erpnext/quality_management/doctype/quality_meeting_agenda/test_quality_meeting_agenda.py
+++ b/erpnext/quality_management/doctype/quality_meeting_agenda/test_quality_meeting_agenda.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestQualityMeetingAgenda(unittest.TestCase):
pass
diff --git a/erpnext/quality_management/doctype/quality_meeting_minutes/quality_meeting_minutes.py b/erpnext/quality_management/doctype/quality_meeting_minutes/quality_meeting_minutes.py
index 47b2c95..e3d061b 100644
--- a/erpnext/quality_management/doctype/quality_meeting_minutes/quality_meeting_minutes.py
+++ b/erpnext/quality_management/doctype/quality_meeting_minutes/quality_meeting_minutes.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityMeetingMinutes(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
index ac87622..fd2b6a4 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
@@ -19,4 +19,4 @@
};
});
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index 53f4e6c..56293c9 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils.nestedset import NestedSet, rebuild_tree
from frappe import _
+from frappe.utils.nestedset import NestedSet
+
class QualityProcedure(NestedSet):
nsm_parent_field = 'parent_quality_procedure'
@@ -77,4 +79,4 @@
if args.parent_quality_procedure == 'All Quality Procedures':
args.parent_quality_procedure = None
- return frappe.get_doc(args).insert()
\ No newline at end of file
+ return frappe.get_doc(args).insert()
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
index eeb4cf6..2851fcc 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
@@ -31,4 +31,4 @@
onload: function(treeview) {
treeview.make_tree();
},
-};
\ No newline at end of file
+};
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 36bdf26..b064011 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -3,11 +3,13 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
from .quality_procedure import add_node
+
class TestQualityProcedure(unittest.TestCase):
def test_add_node(self):
try:
@@ -47,4 +49,4 @@
processes = [
dict(process_description = 'Test Step 1')
]
- )).insert()
\ No newline at end of file
+ )).insert()
diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.py b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.py
index 0d9a286..e281294 100644
--- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.py
+++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityProcedureProcess(Document):
pass
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.js b/erpnext/quality_management/doctype/quality_review/quality_review.js
index 67371bf..0e6b703 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.js
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.js
@@ -22,4 +22,4 @@
}
});
},
-});
\ No newline at end of file
+});
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py
index e3a8b07..b766623 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class QualityReview(Document):
def validate(self):
# fetch targets from goal
@@ -61,4 +63,4 @@
if month in ["January", "April", "July", "October"]:
return True
else:
- return False
\ No newline at end of file
+ return False
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review_list.js b/erpnext/quality_management/doctype/quality_review/quality_review_list.js
index e2eb31b..b0be783 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review_list.js
+++ b/erpnext/quality_management/doctype/quality_review/quality_review_list.js
@@ -9,4 +9,4 @@
return [__("Action Initialised"), "red", "action,=,Action Initialised"];
}
}
-};
\ No newline at end of file
+};
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 a7d92da..2f28dda 100644
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
@@ -3,12 +3,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
from ..quality_goal.test_quality_goal import get_quality_goal
from .quality_review import review
+
class TestQualityReview(unittest.TestCase):
def test_review_creation(self):
quality_goal = get_quality_goal()
@@ -19,4 +21,4 @@
self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target)
quality_review.delete()
- quality_goal.delete()
\ No newline at end of file
+ quality_goal.delete()
diff --git a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.py b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.py
index 3092a1e..23b11e8 100644
--- a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.py
+++ b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityReviewObjective(Document):
pass
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index faa5912..d7dcbf4 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
from erpnext import get_region
+
def check_deletion_permission(doc, method):
region = get_region(doc.company)
if region in ["Nepal", "France"] and doc.docstatus != 0:
@@ -28,3 +31,4 @@
"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 9f318de..1b4087d 100644
--- a/erpnext/regional/address_template/setup.py
+++ b/erpnext/regional/address_template/setup.py
@@ -10,7 +10,7 @@
def get_address_templates():
"""
Return country and path for all HTML files in this directory.
-
+
Returns a list of dicts.
"""
def country(file_name):
diff --git a/erpnext/regional/address_template/templates/germany.html b/erpnext/regional/address_template/templates/germany.html
index 7fa4c32..25c9c9d 100644
--- a/erpnext/regional/address_template/templates/germany.html
+++ b/erpnext/regional/address_template/templates/germany.html
@@ -3,6 +3,6 @@
{% if country in ["Germany", "Deutschland"] %}
{{ pincode }} {{ city }}
{% else %}
- {{ pincode }} {{ city | upper }}<br>
+ {{ pincode }} {{ city | upper }}<br>
{{ country | upper }}
{% endif %}
diff --git a/erpnext/regional/address_template/test_regional_address_template.py b/erpnext/regional/address_template/test_regional_address_template.py
index 8a05ea2..2880d62 100644
--- a/erpnext/regional/address_template/test_regional_address_template.py
+++ b/erpnext/regional/address_template/test_regional_address_template.py
@@ -1,9 +1,11 @@
from __future__ import unicode_literals
+
from unittest import TestCase
import frappe
-from erpnext.regional.address_template.setup import get_address_templates
-from erpnext.regional.address_template.setup import update_address_template
+
+from erpnext.regional.address_template.setup import get_address_templates, update_address_template
+
def ensure_country(country):
if frappe.db.exists("Country", country):
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.py b/erpnext/regional/doctype/datev_settings/datev_settings.py
index cff5bba..0d2d9eb 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.py
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# 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
index 0271329..73412f7 100644
--- a/erpnext/regional/doctype/datev_settings/test_datev_settings.py
+++ b/erpnext/regional/doctype/datev_settings/test_datev_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestDATEVSettings(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js
deleted file mode 100644
index 7b7ba96..0000000
--- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('E Invoice Request Log', {
- // refresh: function(frm) {
-
- // }
-});
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
deleted file mode 100644
index 3034370..0000000
--- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
+++ /dev/null
@@ -1,102 +0,0 @@
-{
- "actions": [],
- "autoname": "EINV-REQ-.#####",
- "creation": "2020-12-08 12:54:08.175992",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "user",
- "url",
- "headers",
- "response",
- "column_break_7",
- "timestamp",
- "reference_invoice",
- "data"
- ],
- "fields": [
- {
- "fieldname": "user",
- "fieldtype": "Link",
- "label": "User",
- "options": "User"
- },
- {
- "fieldname": "reference_invoice",
- "fieldtype": "Data",
- "label": "Reference Invoice"
- },
- {
- "fieldname": "headers",
- "fieldtype": "Code",
- "label": "Headers",
- "options": "JSON"
- },
- {
- "fieldname": "data",
- "fieldtype": "Code",
- "label": "Data",
- "options": "JSON"
- },
- {
- "default": "Now",
- "fieldname": "timestamp",
- "fieldtype": "Datetime",
- "label": "Timestamp"
- },
- {
- "fieldname": "response",
- "fieldtype": "Code",
- "label": "Response",
- "options": "JSON"
- },
- {
- "fieldname": "url",
- "fieldtype": "Data",
- "label": "URL"
- },
- {
- "fieldname": "column_break_7",
- "fieldtype": "Column Break"
- }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-01-13 12:06:57.253111",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "E Invoice Request Log",
- "owner": "Administrator",
- "permissions": [
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1
- },
- {
- "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
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py
deleted file mode 100644
index 9150bdd..0000000
--- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class EInvoiceRequestLog(Document):
- pass
diff --git a/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py b/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py
deleted file mode 100644
index c84e9a2..0000000
--- a/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestEInvoiceRequestLog(unittest.TestCase):
- pass
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
deleted file mode 100644
index 54e4886..0000000
--- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('E Invoice Settings', {
- refresh(frm) {
- const docs_link = 'https://docs.erpnext.com/docs/v13/user/manual/en/regional/india/setup-e-invoicing';
- frm.dashboard.set_headline(
- __("Read {0} for more information on E Invoicing features.", [`<a href='${docs_link}'>documentation</a>`])
- );
- }
-});
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
deleted file mode 100644
index 68ed339..0000000
--- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
+++ /dev/null
@@ -1,73 +0,0 @@
-{
- "actions": [],
- "creation": "2020-09-24 16:23:16.235722",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "enable",
- "section_break_2",
- "sandbox_mode",
- "applicable_from",
- "credentials",
- "auth_token",
- "token_expiry"
- ],
- "fields": [
- {
- "default": "0",
- "fieldname": "enable",
- "fieldtype": "Check",
- "label": "Enable"
- },
- {
- "depends_on": "enable",
- "fieldname": "section_break_2",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "auth_token",
- "fieldtype": "Data",
- "hidden": 1,
- "read_only": 1
- },
- {
- "fieldname": "token_expiry",
- "fieldtype": "Datetime",
- "hidden": 1,
- "read_only": 1
- },
- {
- "fieldname": "credentials",
- "fieldtype": "Table",
- "label": "Credentials",
- "mandatory_depends_on": "enable",
- "options": "E Invoice User"
- },
- {
- "default": "0",
- "fieldname": "sandbox_mode",
- "fieldtype": "Check",
- "label": "Sandbox Mode"
- },
- {
- "fieldname": "applicable_from",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Applicable From",
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "issingle": 1,
- "links": [],
- "modified": "2021-03-30 12:26:25.538294",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "E Invoice Settings",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py
deleted file mode 100644
index c24ad88..0000000
--- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-from __future__ import unicode_literals
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-
-class EInvoiceSettings(Document):
- def validate(self):
- if self.enable and not self.credentials:
- frappe.throw(_('You must add atleast one credentials to be able to use E Invoicing.'))
-
diff --git a/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py
deleted file mode 100644
index a11ce63..0000000
--- a/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-# import frappe
-import unittest
-
-class TestEInvoiceSettings(unittest.TestCase):
- pass
diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
deleted file mode 100644
index a65b1ca..0000000
--- a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "actions": [],
- "creation": "2020-12-22 15:02:46.229474",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "company",
- "gstin",
- "username",
- "password"
- ],
- "fields": [
- {
- "fieldname": "gstin",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "GSTIN",
- "reqd": 1
- },
- {
- "fieldname": "username",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Username",
- "reqd": 1
- },
- {
- "fieldname": "password",
- "fieldtype": "Password",
- "in_list_view": 1,
- "label": "Password",
- "reqd": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Company",
- "options": "Company",
- "reqd": 1
- }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-03-22 12:16:56.365616",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "E Invoice User",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py
deleted file mode 100644
index 056c54f..0000000
--- a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-# import frappe
-from frappe.model.document import Document
-
-class EInvoiceUser(Document):
- pass
diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js
index 7ff4de4..347fdfe 100644
--- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js
+++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js
@@ -25,4 +25,4 @@
});
}
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
index 86cd4d1..0704de8 100644
--- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
+++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class GSTHSNCode(Document):
pass
@@ -30,4 +32,4 @@
'tax_category': tax.tax_category,
'valid_from': tax.valid_from
})
- item_to_be_updated.save()
\ No newline at end of file
+ item_to_be_updated.save()
diff --git a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js b/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js
deleted file mode 100644
index 24c5fd3..0000000
--- a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GST HSN Code", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new GST HSN Code
- () => frappe.tests.make('GST HSN Code', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.py b/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.py
index ed54f20..1a90e6d 100644
--- a/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.py
+++ b/erpnext/regional/doctype/gst_hsn_code/test_gst_hsn_code.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestGSTHSNCode(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.py b/erpnext/regional/doctype/gst_settings/gst_settings.py
index af3d92e..7b27fb6 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.py
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.py
@@ -3,11 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, os
+
+import os
+
+import frappe
from frappe import _
-from frappe.utils import get_url, nowdate, date_diff
-from frappe.model.document import Document
from frappe.contacts.doctype.contact.contact import get_default_contact
+from frappe.model.document import Document
+from frappe.utils import date_diff, get_url, nowdate
+
class EmailMissing(frappe.ValidationError): pass
diff --git a/erpnext/regional/doctype/gst_settings/test_gst_settings.js b/erpnext/regional/doctype/gst_settings/test_gst_settings.js
deleted file mode 100644
index 00fcca6..0000000
--- a/erpnext/regional/doctype/gst_settings/test_gst_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: GST Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new GST Settings
- () => frappe.tests.make('GST Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/regional/doctype/gst_settings/test_gst_settings.py b/erpnext/regional/doctype/gst_settings/test_gst_settings.py
index d118dee..836d3a8 100644
--- a/erpnext/regional/doctype/gst_settings/test_gst_settings.py
+++ b/erpnext/regional/doctype/gst_settings/test_gst_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestGSTSettings(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html
index 3b6a45a..f3fc60f 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html
@@ -294,4 +294,4 @@
text-align: right;
}
-</style>
\ No newline at end of file
+</style>
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index 0ee5b09..d8ce319 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -3,15 +3,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import os
+
import json
+import os
+
import frappe
-from six import iteritems
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, cstr
+from frappe.utils import cstr, flt
+from six import iteritems
+
from erpnext.regional.india import state_numbers
+
class GSTR3BReport(Document):
def validate(self):
self.get_data()
diff --git a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
index 065f80d..115f9b8 100644
--- a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
@@ -3,13 +3,15 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
-from frappe.utils import getdate
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
-from erpnext.stock.doctype.item.test_item import make_item
import json
+import unittest
+
+import frappe
+from frappe.utils import getdate
+
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.stock.doctype.item.test_item import make_item
test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js
index c2d6edf..5918ec8 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js
@@ -43,4 +43,4 @@
}
}
-});
\ No newline at end of file
+});
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 0030053..76cb621 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
+++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py
@@ -3,21 +3,21 @@
from __future__ import unicode_literals
-from decimal import Decimal
-import json
import re
-import traceback
import zipfile
-import frappe, erpnext
+
+import dateutil
+import frappe
+from bs4 import BeautifulSoup as bs
from frappe import _
from frappe.model.document import Document
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.utils import flt, get_datetime_str, today
from frappe.utils.data import format_datetime
-from bs4 import BeautifulSoup as bs
-from frappe.utils import cint, flt, today, nowdate, add_days, get_files_path, get_datetime_str
-import dateutil
from frappe.utils.file_manager import save_file
+import erpnext
+
+
class ImportSupplierInvoice(Document):
def validate(self):
if not frappe.db.get_value("Stock Settings", fieldname="stock_uom"):
diff --git a/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py
index d1caf77..4be4c3a 100644
--- a/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py
+++ b/erpnext/regional/doctype/import_supplier_invoice/test_import_supplier_invoice.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestImportSupplierInvoice(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/e_invoice_request_log/__init__.py b/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
similarity index 100%
rename from erpnext/regional/doctype/e_invoice_request_log/__init__.py
rename to erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
new file mode 100644
index 0000000..89ba3e9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "creation": "2021-07-13 09:17:09.862163",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "title",
+ "item_tax_template",
+ "account"
+ ],
+ "fields": [
+ {
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Account",
+ "options": "Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Title",
+ "reqd": 1
+ },
+ {
+ "fieldname": "item_tax_template",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Tax Template",
+ "options": "Item Tax Template",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-04 06:42:38.205597",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Purchase Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
new file mode 100644
index 0000000..3920bc5
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATPurchaseAccount(Document):
+ pass
diff --git a/erpnext/regional/doctype/e_invoice_request_log/__init__.py b/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
similarity index 100%
copy from erpnext/regional/doctype/e_invoice_request_log/__init__.py
copy to erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
new file mode 100644
index 0000000..72613f4
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Sales Account', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
new file mode 100644
index 0000000..df27478
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "creation": "2021-07-13 08:46:33.820968",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "title",
+ "item_tax_template",
+ "account"
+ ],
+ "fields": [
+ {
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Account",
+ "options": "Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Title",
+ "reqd": 1
+ },
+ {
+ "fieldname": "item_tax_template",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Tax Template",
+ "options": "Item Tax Template",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-04 06:42:00.081407",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Sales Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
new file mode 100644
index 0000000..7c2689f
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSalesAccount(Document):
+ pass
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
new file mode 100644
index 0000000..1d6a6a7
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSalesAccount(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/doctype/e_invoice_user/__init__.py b/erpnext/regional/doctype/ksa_vat_setting/__init__.py
similarity index 100%
rename from erpnext/regional/doctype/e_invoice_user/__init__.py
rename to erpnext/regional/doctype/ksa_vat_setting/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
new file mode 100644
index 0000000..00b62b9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Setting', {
+ onload: function () {
+ frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting');
+ }
+});
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
new file mode 100644
index 0000000..3361946
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2021-07-13 08:49:01.100356",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "ksa_vat_sales_accounts",
+ "ksa_vat_purchase_accounts"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "ksa_vat_sales_accounts",
+ "fieldtype": "Table",
+ "label": "KSA VAT Sales Accounts",
+ "options": "KSA VAT Sales Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "ksa_vat_purchase_accounts",
+ "fieldtype": "Table",
+ "label": "KSA VAT Purchase Accounts",
+ "options": "KSA VAT Purchase Account",
+ "reqd": 1
+ }
+ ],
+ "links": [],
+ "modified": "2021-08-26 04:29:06.499378",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Setting",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "company",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
new file mode 100644
index 0000000..bdae116
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSetting(Document):
+ pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
new file mode 100644
index 0000000..269cbec
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
@@ -0,0 +1,5 @@
+frappe.listview_settings['KSA VAT Setting'] = {
+ onload () {
+ frappe.breadcrumbs.add('Accounts');
+ }
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
new file mode 100644
index 0000000..7207901
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSetting(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index ad60db0..d8553f1 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -3,17 +3,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import getdate, get_link_to_form
from frappe.model.document import Document
+from frappe.utils import get_link_to_form, getdate
+
from erpnext.accounts.utils import get_fiscal_year
+
class LowerDeductionCertificate(Document):
def validate(self):
self.validate_dates()
self.validate_supplier_against_section_code()
-
+
def validate_dates(self):
if getdate(self.valid_upto) < getdate(self.valid_from):
frappe.throw(_("Valid Upto date cannot be before Valid From date"))
@@ -44,4 +47,4 @@
return True
elif getdate(self.valid_from) <= valid_from and valid_upto <= getdate(self.valid_upto):
return True
- return False
\ No newline at end of file
+ return False
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py
index 7e95020..54443c0 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/test_lower_deduction_certificate.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestLowerDeductionCertificate(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/e_invoice_settings/__init__.py b/erpnext/regional/doctype/product_tax_category/__init__.py
similarity index 100%
rename from erpnext/regional/doctype/e_invoice_settings/__init__.py
rename to erpnext/regional/doctype/product_tax_category/__init__.py
diff --git a/erpnext/regional/doctype/product_tax_category/product_tax_category.js b/erpnext/regional/doctype/product_tax_category/product_tax_category.js
new file mode 100644
index 0000000..9f8e795
--- /dev/null
+++ b/erpnext/regional/doctype/product_tax_category/product_tax_category.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Product Tax Category', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/regional/doctype/product_tax_category/product_tax_category.json b/erpnext/regional/doctype/product_tax_category/product_tax_category.json
new file mode 100644
index 0000000..147cb34
--- /dev/null
+++ b/erpnext/regional/doctype/product_tax_category/product_tax_category.json
@@ -0,0 +1,70 @@
+{
+ "actions": [],
+ "autoname": "field:product_tax_code",
+ "creation": "2021-08-23 12:33:37.910225",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "product_tax_code",
+ "column_break_2",
+ "category_name",
+ "section_break_4",
+ "description"
+ ],
+ "fields": [
+ {
+ "fieldname": "product_tax_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Product Tax Code",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Description"
+ },
+ {
+ "fieldname": "category_name",
+ "fieldtype": "Data",
+ "label": "Category Name",
+ "length": 255
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-08-24 09:10:25.313642",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Product Tax Category",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "category_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/product_tax_category/product_tax_category.py b/erpnext/regional/doctype/product_tax_category/product_tax_category.py
new file mode 100644
index 0000000..b6be9e0
--- /dev/null
+++ b/erpnext/regional/doctype/product_tax_category/product_tax_category.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class ProductTaxCategory(Document):
+ pass
diff --git a/erpnext/regional/doctype/product_tax_category/test_product_tax_category.py b/erpnext/regional/doctype/product_tax_category/test_product_tax_category.py
new file mode 100644
index 0000000..2668f23
--- /dev/null
+++ b/erpnext/regional/doctype/product_tax_category/test_product_tax_category.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestProductTaxCategory(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py
index d74154b..4c3e8a7 100644
--- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py
+++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class SouthAfricaVATSettings(Document):
pass
diff --git a/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py
index 1c36652..0f19f25 100644
--- a/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py
+++ b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py
@@ -4,5 +4,6 @@
# import frappe
import unittest
+
class TestSouthAfricaVATSettings(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
index 41a0f11..64b2ec5 100644
--- a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.model.document import Document
-from frappe.utils import getdate, flt, get_link_to_form
-from erpnext.accounts.utils import get_fiscal_year
from frappe.contacts.doctype.address.address import get_company_address
+from frappe.model.document import Document
+from frappe.utils import flt, get_link_to_form, getdate
+
+from erpnext.accounts.utils import get_fiscal_year
+
class TaxExemption80GCertificate(Document):
def validate(self):
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
index c478b0f..74e9ced 100644
--- a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
@@ -3,14 +3,21 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
from frappe.utils import getdate
+
from erpnext.accounts.utils import get_fiscal_year
-from erpnext.non_profit.doctype.donation.test_donation import create_donor, create_mode_of_payment, create_donor_type
from erpnext.non_profit.doctype.donation.donation import create_donation
-from erpnext.non_profit.doctype.membership.test_membership import setup_membership, make_membership
+from erpnext.non_profit.doctype.donation.test_donation import (
+ create_donor,
+ create_donor_type,
+ create_mode_of_payment,
+)
from erpnext.non_profit.doctype.member.member import create_member
+from erpnext.non_profit.doctype.membership.test_membership import make_membership, setup_membership
+
class TestTaxExemption80GCertificate(unittest.TestCase):
def setUp(self):
@@ -98,4 +105,4 @@
certificate.update(args)
- return certificate
\ No newline at end of file
+ return certificate
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
index bdad798..76d8912 100644
--- a/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class TaxExemption80GCertificateDetail(Document):
pass
diff --git a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
index 80d6b3a..a1b27d7 100644
--- a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
+++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class UAEVATAccount(Document):
pass
diff --git a/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
index b88439f..cec30e6 100644
--- a/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
+++ b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestUAEVATSettings(unittest.TestCase):
pass
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
index 20dc604..1bf37dd 100644
--- a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class UAEVATSettings(Document):
pass
diff --git a/erpnext/regional/france/utils.py b/erpnext/regional/france/utils.py
index 424615d..63c5a1f 100644
--- a/erpnext/regional/france/utils.py
+++ b/erpnext/regional/france/utils.py
@@ -2,7 +2,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
# don't remove this function it is used in tests
def test_method():
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
index c1fa6e4..a68cecc 100644
--- a/erpnext/regional/germany/setup.py
+++ b/erpnext/regional/germany/setup.py
@@ -1,4 +1,3 @@
-import os
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py
index 63f9a77..be3d7a3 100644
--- a/erpnext/regional/germany/utils/datev/datev_constants.py
+++ b/erpnext/regional/germany/utils/datev/datev_constants.py
@@ -455,7 +455,7 @@
"Konto",
# Account name
"Kontenbeschriftung",
- # Language of the account name
+ # Language of the account name
# "de-DE" or "en-GB"
"Sprach-ID"
]
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index 122c15f..9d1fabb 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -4,12 +4,12 @@
import datetime
import zipfile
from csv import QUOTE_NONNUMERIC
-from six import BytesIO
-import six
import frappe
import pandas as pd
from frappe import _
+from six import BytesIO
+
from .datev_constants import DataCategory
@@ -33,6 +33,14 @@
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'
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index faeb36f..5c4d308 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
from six import iteritems
states = [
diff --git a/erpnext/regional/india/e_invoice/__init__.py b/erpnext/regional/india/e_invoice/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/india/e_invoice/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/india/e_invoice/einv_item_template.json b/erpnext/regional/india/e_invoice/einv_item_template.json
deleted file mode 100644
index 78e5651..0000000
--- a/erpnext/regional/india/e_invoice/einv_item_template.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{{
- "SlNo": "{item.sr_no}",
- "PrdDesc": "{item.description}",
- "IsServc": "{item.is_service_item}",
- "HsnCd": "{item.gst_hsn_code}",
- "Barcde": "{item.barcode}",
- "Unit": "{item.uom}",
- "Qty": "{item.qty}",
- "FreeQty": "{item.free_qty}",
- "UnitPrice": "{item.unit_rate}",
- "TotAmt": "{item.gross_amount}",
- "Discount": "{item.discount_amount}",
- "AssAmt": "{item.taxable_value}",
- "PrdSlNo": "{item.serial_no}",
- "GstRt": "{item.tax_rate}",
- "IgstAmt": "{item.igst_amount}",
- "CgstAmt": "{item.cgst_amount}",
- "SgstAmt": "{item.sgst_amount}",
- "CesRt": "{item.cess_rate}",
- "CesAmt": "{item.cess_amount}",
- "CesNonAdvlAmt": "{item.cess_nadv_amount}",
- "StateCesRt": "{item.state_cess_rate}",
- "StateCesAmt": "{item.state_cess_amount}",
- "StateCesNonAdvlAmt": "{item.state_cess_nadv_amount}",
- "OthChrg": "{item.other_charges}",
- "TotItemVal": "{item.total_value}",
- "BchDtls": {{
- "Nm": "{item.batch_no}",
- "ExpDt": "{item.batch_expiry_date}"
- }}
-}}
\ No newline at end of file
diff --git a/erpnext/regional/india/e_invoice/einv_template.json b/erpnext/regional/india/e_invoice/einv_template.json
deleted file mode 100644
index 60f490d..0000000
--- a/erpnext/regional/india/e_invoice/einv_template.json
+++ /dev/null
@@ -1,110 +0,0 @@
-{{
- "Version": "1.1",
- "TranDtls": {{
- "TaxSch": "{transaction_details.tax_scheme}",
- "SupTyp": "{transaction_details.supply_type}",
- "RegRev": "{transaction_details.reverse_charge}",
- "EcmGstin": "{transaction_details.ecom_gstin}",
- "IgstOnIntra": "{transaction_details.igst_on_intra}"
- }},
- "DocDtls": {{
- "Typ": "{doc_details.invoice_type}",
- "No": "{doc_details.invoice_name}",
- "Dt": "{doc_details.invoice_date}"
- }},
- "SellerDtls": {{
- "Gstin": "{seller_details.gstin}",
- "LglNm": "{seller_details.legal_name}",
- "TrdNm": "{seller_details.trade_name}",
- "Loc": "{seller_details.location}",
- "Pin": "{seller_details.pincode}",
- "Stcd": "{seller_details.state_code}",
- "Addr1": "{seller_details.address_line1}",
- "Addr2": "{seller_details.address_line2}",
- "Ph": "{seller_details.phone}",
- "Em": "{seller_details.email}"
- }},
- "BuyerDtls": {{
- "Gstin": "{buyer_details.gstin}",
- "LglNm": "{buyer_details.legal_name}",
- "TrdNm": "{buyer_details.trade_name}",
- "Addr1": "{buyer_details.address_line1}",
- "Addr2": "{buyer_details.address_line2}",
- "Loc": "{buyer_details.location}",
- "Pin": "{buyer_details.pincode}",
- "Stcd": "{buyer_details.state_code}",
- "Ph": "{buyer_details.phone}",
- "Em": "{buyer_details.email}",
- "Pos": "{buyer_details.place_of_supply}"
- }},
- "DispDtls": {{
- "Nm": "{dispatch_details.company_name}",
- "Addr1": "{dispatch_details.address_line1}",
- "Addr2": "{dispatch_details.address_line2}",
- "Loc": "{dispatch_details.location}",
- "Pin": "{dispatch_details.pincode}",
- "Stcd": "{dispatch_details.state_code}"
- }},
- "ShipDtls": {{
- "Gstin": "{shipping_details.gstin}",
- "LglNm": "{shipping_details.legal_name}",
- "TrdNm": "{shipping_details.trader_name}",
- "Addr1": "{shipping_details.address_line1}",
- "Addr2": "{shipping_details.address_line2}",
- "Loc": "{shipping_details.location}",
- "Pin": "{shipping_details.pincode}",
- "Stcd": "{shipping_details.state_code}"
- }},
- "ItemList": [
- {item_list}
- ],
- "ValDtls": {{
- "AssVal": "{invoice_value_details.base_total}",
- "CgstVal": "{invoice_value_details.total_cgst_amt}",
- "SgstVal": "{invoice_value_details.total_sgst_amt}",
- "IgstVal": "{invoice_value_details.total_igst_amt}",
- "CesVal": "{invoice_value_details.total_cess_amt}",
- "Discount": "{invoice_value_details.invoice_discount_amt}",
- "RndOffAmt": "{invoice_value_details.round_off}",
- "OthChrg": "{invoice_value_details.total_other_charges}",
- "TotInvVal": "{invoice_value_details.base_grand_total}",
- "TotInvValFc": "{invoice_value_details.grand_total}"
- }},
- "PayDtls": {{
- "Nm": "{payment_details.payee_name}",
- "AccDet": "{payment_details.account_no}",
- "Mode": "{payment_details.mode_of_payment}",
- "FinInsBr": "{payment_details.ifsc_code}",
- "PayTerm": "{payment_details.terms}",
- "PaidAmt": "{payment_details.paid_amount}",
- "PaymtDue": "{payment_details.outstanding_amount}"
- }},
- "RefDtls": {{
- "DocPerdDtls": {{
- "InvStDt": "{period_details.start_date}",
- "InvEndDt": "{period_details.end_date}"
- }},
- "PrecDocDtls": [{{
- "InvNo": "{prev_doc_details.invoice_name}",
- "InvDt": "{prev_doc_details.invoice_date}"
- }}]
- }},
- "ExpDtls": {{
- "ShipBNo": "{export_details.bill_no}",
- "ShipBDt": "{export_details.bill_date}",
- "Port": "{export_details.port}",
- "ForCur": "{export_details.foreign_curr_code}",
- "CntCode": "{export_details.country_code}",
- "ExpDuty": "{export_details.export_duty}"
- }},
- "EwbDtls": {{
- "TransId": "{eway_bill_details.gstin}",
- "TransName": "{eway_bill_details.name}",
- "TransMode": "{eway_bill_details.mode_of_transport}",
- "Distance": "{eway_bill_details.distance}",
- "TransDocNo": "{eway_bill_details.document_name}",
- "TransDocDt": "{eway_bill_details.document_date}",
- "VehNo": "{eway_bill_details.vehicle_no}",
- "VehType": "{eway_bill_details.vehicle_type}"
- }}
-}}
\ No newline at end of file
diff --git a/erpnext/regional/india/e_invoice/einv_validation.json b/erpnext/regional/india/e_invoice/einv_validation.json
deleted file mode 100644
index f4a3542..0000000
--- a/erpnext/regional/india/e_invoice/einv_validation.json
+++ /dev/null
@@ -1,957 +0,0 @@
-{
- "Version": {
- "type": "string",
- "minLength": 1,
- "maxLength": 6,
- "description": "Version of the schema"
- },
- "Irn": {
- "type": "string",
- "minLength": 64,
- "maxLength": 64,
- "description": "Invoice Reference Number"
- },
- "TranDtls": {
- "type": "object",
- "properties": {
- "TaxSch": {
- "type": "string",
- "minLength": 3,
- "maxLength": 10,
- "enum": ["GST"],
- "description": "GST- Goods and Services Tax Scheme"
- },
- "SupTyp": {
- "type": "string",
- "minLength": 3,
- "maxLength": 10,
- "enum": ["B2B", "SEZWP", "SEZWOP", "EXPWP", "EXPWOP", "DEXP"],
- "description": "Type of Supply: B2B-Business to Business, SEZWP - SEZ with payment, SEZWOP - SEZ without payment, EXPWP - Export with Payment, EXPWOP - Export without payment,DEXP - Deemed Export"
- },
- "RegRev": {
- "type": "string",
- "minLength": 1,
- "maxLength": 1,
- "enum": ["Y", "N"],
- "description": "Y- whether the tax liability is payable under reverse charge"
- },
- "EcmGstin": {
- "type": "string",
- "minLength": 15,
- "maxLength": 15,
- "pattern": "([0-9]{2}[0-9A-Z]{13})",
- "description": "E-Commerce GSTIN",
- "validationMsg": "E-Commerce GSTIN is invalid"
- },
- "IgstOnIntra": {
- "type": "string",
- "minLength": 1,
- "maxLength": 1,
- "enum": ["Y", "N"],
- "description": "Y- indicates the supply is intra state but chargeable to IGST"
- }
- },
- "required": ["TaxSch", "SupTyp"]
- },
- "DocDtls": {
- "type": "object",
- "properties": {
- "Typ": {
- "type": "string",
- "minLength": 3,
- "maxLength": 3,
- "enum": ["INV", "CRN", "DBN"],
- "description": "Document Type"
- },
- "No": {
- "type": "string",
- "minLength": 1,
- "maxLength": 16,
- "pattern": "^([A-Z1-9]{1}[A-Z0-9/-]{0,15})$",
- "description": "Document Number",
- "validationMsg": "Document Number should not be starting with 0, / and -"
- },
- "Dt": {
- "type": "string",
- "minLength": 10,
- "maxLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Document Date"
- }
- },
- "required": ["Typ", "No", "Dt"]
- },
- "SellerDtls": {
- "type": "object",
- "properties": {
- "Gstin": {
- "type": "string",
- "minLength": 15,
- "maxLength": 15,
- "pattern": "([0-9]{2}[0-9A-Z]{13})",
- "description": "Supplier GSTIN",
- "validationMsg": "Company GSTIN is invalid"
- },
- "LglNm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Legal Name"
- },
- "TrdNm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Tradename"
- },
- "Addr1": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Address Line 1"
- },
- "Addr2": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Address Line 2"
- },
- "Loc": {
- "type": "string",
- "minLength": 3,
- "maxLength": 50,
- "description": "Location"
- },
- "Pin": {
- "type": "number",
- "minimum": 100000,
- "maximum": 999999,
- "description": "Pincode"
- },
- "Stcd": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2,
- "description": "Supplier State Code"
- },
- "Ph": {
- "type": "string",
- "minLength": 6,
- "maxLength": 12,
- "description": "Phone"
- },
- "Em": {
- "type": "string",
- "minLength": 6,
- "maxLength": 100,
- "description": "Email-Id"
- }
- },
- "required": ["Gstin", "LglNm", "Addr1", "Loc", "Pin", "Stcd"]
- },
- "BuyerDtls": {
- "type": "object",
- "properties": {
- "Gstin": {
- "type": "string",
- "minLength": 3,
- "maxLength": 15,
- "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
- "description": "Buyer GSTIN",
- "validationMsg": "Customer GSTIN is invalid"
- },
- "LglNm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Legal Name"
- },
- "TrdNm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Trade Name"
- },
- "Pos": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2,
- "description": "Place of Supply State code"
- },
- "Addr1": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Address Line 1"
- },
- "Addr2": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Address Line 2"
- },
- "Loc": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Location"
- },
- "Pin": {
- "type": "number",
- "minimum": 100000,
- "maximum": 999999,
- "description": "Pincode"
- },
- "Stcd": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2,
- "description": "Buyer State Code"
- },
- "Ph": {
- "type": "string",
- "minLength": 6,
- "maxLength": 12,
- "description": "Phone"
- },
- "Em": {
- "type": "string",
- "minLength": 6,
- "maxLength": 100,
- "description": "Email-Id"
- }
- },
- "required": ["Gstin", "LglNm", "Pos", "Addr1", "Loc", "Stcd"]
- },
- "DispDtls": {
- "type": "object",
- "properties": {
- "Nm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Dispatch Address Name"
- },
- "Addr1": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Address Line 1"
- },
- "Addr2": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Address Line 2"
- },
- "Loc": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Location"
- },
- "Pin": {
- "type": "number",
- "minimum": 100000,
- "maximum": 999999,
- "description": "Pincode"
- },
- "Stcd": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2,
- "description": "State Code"
- }
- },
- "required": ["Nm", "Addr1", "Loc", "Pin", "Stcd"]
- },
- "ShipDtls": {
- "type": "object",
- "properties": {
- "Gstin": {
- "type": "string",
- "maxLength": 15,
- "minLength": 3,
- "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
- "description": "Shipping Address GSTIN",
- "validationMsg": "Shipping Address GSTIN is invalid"
- },
- "LglNm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Legal Name"
- },
- "TrdNm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Trade Name"
- },
- "Addr1": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Address Line 1"
- },
- "Addr2": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Address Line 2"
- },
- "Loc": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Location"
- },
- "Pin": {
- "type": "number",
- "minimum": 100000,
- "maximum": 999999,
- "description": "Pincode"
- },
- "Stcd": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2,
- "description": "State Code"
- }
- },
- "required": ["LglNm", "Addr1", "Loc", "Pin", "Stcd"]
- },
- "ItemList": {
- "type": "Array",
- "properties": {
- "SlNo": {
- "type": "string",
- "minLength": 1,
- "maxLength": 6,
- "description": "Serial No. of Item"
- },
- "PrdDesc": {
- "type": "string",
- "minLength": 3,
- "maxLength": 300,
- "description": "Item Name"
- },
- "IsServc": {
- "type": "string",
- "minLength": 1,
- "maxLength": 1,
- "enum": ["Y", "N"],
- "description": "Is Service Item"
- },
- "HsnCd": {
- "type": "string",
- "minLength": 4,
- "maxLength": 8,
- "description": "HSN Code"
- },
- "Barcde": {
- "type": "string",
- "minLength": 3,
- "maxLength": 30,
- "description": "Barcode"
- },
- "Qty": {
- "type": "number",
- "minimum": 0,
- "maximum": 9999999999.999,
- "description": "Quantity"
- },
- "FreeQty": {
- "type": "number",
- "minimum": 0,
- "maximum": 9999999999.999,
- "description": "Free Quantity"
- },
- "Unit": {
- "type": "string",
- "minLength": 3,
- "maxLength": 8,
- "description": "UOM"
- },
- "UnitPrice": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.999,
- "description": "Rate"
- },
- "TotAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Gross Amount"
- },
- "Discount": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Discount"
- },
- "PreTaxVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Pre tax value"
- },
- "AssAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Taxable Value"
- },
- "GstRt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999.999,
- "description": "GST Rate"
- },
- "IgstAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "IGST Amount"
- },
- "CgstAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "CGST Amount"
- },
- "SgstAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "SGST Amount"
- },
- "CesRt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999.999,
- "description": "Cess Rate"
- },
- "CesAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Cess Amount (Advalorem)"
- },
- "CesNonAdvlAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Cess Amount (Non-Advalorem)"
- },
- "StateCesRt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999.999,
- "description": "State CESS Rate"
- },
- "StateCesAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "State CESS Amount"
- },
- "StateCesNonAdvlAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "State CESS Amount (Non Advalorem)"
- },
- "OthChrg": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Other Charges"
- },
- "TotItemVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Total Item Value"
- },
- "OrdLineRef": {
- "type": "string",
- "minLength": 1,
- "maxLength": 50,
- "description": "Order line reference"
- },
- "OrgCntry": {
- "type": "string",
- "minLength": 2,
- "maxLength": 2,
- "description": "Origin Country"
- },
- "PrdSlNo": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "description": "Serial number"
- },
- "BchDtls": {
- "type": "object",
- "properties": {
- "Nm": {
- "type": "string",
- "minLength": 3,
- "maxLength": 20,
- "description": "Batch number"
- },
- "ExpDt": {
- "type": "string",
- "maxLength": 10,
- "minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Batch Expiry Date"
- },
- "WrDt": {
- "type": "string",
- "maxLength": 10,
- "minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Warranty Date"
- }
- },
- "required": ["Nm"]
- },
- "AttribDtls": {
- "type": "Array",
- "Attribute": {
- "type": "object",
- "properties": {
- "Nm": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Attribute name of the item"
- },
- "Val": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Attribute value of the item"
- }
- }
- }
- }
- },
- "required": [
- "SlNo",
- "IsServc",
- "HsnCd",
- "UnitPrice",
- "TotAmt",
- "AssAmt",
- "GstRt",
- "TotItemVal"
- ]
- },
- "ValDtls": {
- "type": "object",
- "properties": {
- "AssVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Total Assessable value of all items"
- },
- "CgstVal": {
- "type": "number",
- "maximum": 99999999999999.99,
- "minimum": 0,
- "description": "Total CGST value of all items"
- },
- "SgstVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Total SGST value of all items"
- },
- "IgstVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Total IGST value of all items"
- },
- "CesVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Total CESS value of all items"
- },
- "StCesVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Total State CESS value of all items"
- },
- "Discount": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Invoice Discount"
- },
- "OthChrg": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Other Charges"
- },
- "RndOffAmt": {
- "type": "number",
- "minimum": -99.99,
- "maximum": 99.99,
- "description": "Rounded off Amount"
- },
- "TotInvVal": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Final Invoice Value "
- },
- "TotInvValFc": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Final Invoice value in Foreign Currency"
- }
- },
- "required": ["AssVal", "TotInvVal"]
- },
- "PayDtls": {
- "type": "object",
- "properties": {
- "Nm": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Payee Name"
- },
- "AccDet": {
- "type": "string",
- "minLength": 1,
- "maxLength": 18,
- "description": "Bank Account Number of Payee"
- },
- "Mode": {
- "type": "string",
- "minLength": 1,
- "maxLength": 18,
- "description": "Mode of Payment"
- },
- "FinInsBr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 11,
- "description": "Branch or IFSC code"
- },
- "PayTerm": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Terms of Payment"
- },
- "PayInstr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Payment Instruction"
- },
- "CrTrn": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Credit Transfer"
- },
- "DirDr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "Direct Debit"
- },
- "CrDay": {
- "type": "number",
- "minimum": 0,
- "maximum": 9999,
- "description": "Credit Days"
- },
- "PaidAmt": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Advance Amount"
- },
- "PaymtDue": {
- "type": "number",
- "minimum": 0,
- "maximum": 99999999999999.99,
- "description": "Outstanding Amount"
- }
- }
- },
- "RefDtls": {
- "type": "object",
- "properties": {
- "InvRm": {
- "type": "string",
- "maxLength": 100,
- "minLength": 3,
- "pattern": "^[0-9A-Za-z/-]{3,100}$",
- "description": "Remarks/Note"
- },
- "DocPerdDtls": {
- "type": "object",
- "properties": {
- "InvStDt": {
- "type": "string",
- "maxLength": 10,
- "minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Invoice Period Start Date"
- },
- "InvEndDt": {
- "type": "string",
- "maxLength": 10,
- "minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Invoice Period End Date"
- }
- },
- "required": ["InvStDt ", "InvEndDt "]
- },
- "PrecDocDtls": {
- "type": "object",
- "properties": {
- "InvNo": {
- "type": "string",
- "minLength": 1,
- "maxLength": 16,
- "pattern": "^[1-9A-Z]{1}[0-9A-Z/-]{1,15}$",
- "description": "Reference of Original Invoice"
- },
- "InvDt": {
- "type": "string",
- "maxLength": 10,
- "minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Date of Orginal Invoice"
- },
- "OthRefNo": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "description": "Other Reference"
- }
- }
- },
- "required": ["InvNo", "InvDt"],
- "ContrDtls": {
- "type": "object",
- "properties": {
- "RecAdvRefr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "pattern": "^([0-9A-Za-z/-]){1,20}$",
- "description": "Receipt Advice No."
- },
- "RecAdvDt": {
- "type": "string",
- "minLength": 10,
- "maxLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Date of receipt advice"
- },
- "TendRefr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "pattern": "^([0-9A-Za-z/-]){1,20}$",
- "description": "Lot/Batch Reference No."
- },
- "ContrRefr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "pattern": "^([0-9A-Za-z/-]){1,20}$",
- "description": "Contract Reference Number"
- },
- "ExtRefr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "pattern": "^([0-9A-Za-z/-]){1,20}$",
- "description": "Any other reference"
- },
- "ProjRefr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "pattern": "^([0-9A-Za-z/-]){1,20}$",
- "description": "Project Reference Number"
- },
- "PORefr": {
- "type": "string",
- "minLength": 1,
- "maxLength": 16,
- "pattern": "^([0-9A-Za-z/-]){1,16}$",
- "description": "PO Reference Number"
- },
- "PORefDt": {
- "type": "string",
- "minLength": 10,
- "maxLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "PO Reference date"
- }
- }
- }
- }
- },
- "AddlDocDtls": {
- "type": "Array",
- "properties": {
- "Url": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Supporting document URL"
- },
- "Docs": {
- "type": "string",
- "minLength": 3,
- "maxLength": 1000,
- "description": "Supporting document in Base64 Format"
- },
- "Info": {
- "type": "string",
- "minLength": 3,
- "maxLength": 1000,
- "description": "Any additional information"
- }
- }
- },
-
- "ExpDtls": {
- "type": "object",
- "properties": {
- "ShipBNo": {
- "type": "string",
- "minLength": 1,
- "maxLength": 20,
- "description": "Shipping Bill No."
- },
- "ShipBDt": {
- "type": "string",
- "minLength": 10,
- "maxLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Shipping Bill Date"
- },
- "Port": {
- "type": "string",
- "minLength": 2,
- "maxLength": 10,
- "pattern": "^[0-9A-Za-z]{2,10}$",
- "description": "Port Code. Refer the master"
- },
- "RefClm": {
- "type": "string",
- "minLength": 1,
- "maxLength": 1,
- "description": "Claiming Refund. Y/N"
- },
- "ForCur": {
- "type": "string",
- "minLength": 3,
- "maxLength": 16,
- "description": "Additional Currency Code. Refer the master"
- },
- "CntCode": {
- "type": "string",
- "minLength": 2,
- "maxLength": 2,
- "description": "Country Code. Refer the master"
- },
- "ExpDuty": {
- "type": "number",
- "minimum": 0,
- "maximum": 999999999999.99,
- "description": "Export Duty"
- }
- }
- },
- "EwbDtls": {
- "type": "object",
- "properties": {
- "TransId": {
- "type": "string",
- "minLength": 15,
- "maxLength": 15,
- "description": "Transporter GSTIN"
- },
- "TransName": {
- "type": "string",
- "minLength": 3,
- "maxLength": 100,
- "description": "Transporter Name"
- },
- "TransMode": {
- "type": "string",
- "maxLength": 1,
- "minLength": 1,
- "enum": ["1", "2", "3", "4"],
- "description": "Mode of Transport"
- },
- "Distance": {
- "type": "number",
- "minimum": 1,
- "maximum": 9999,
- "description": "Distance"
- },
- "TransDocNo": {
- "type": "string",
- "minLength": 1,
- "maxLength": 15,
- "pattern": "^([0-9A-Z/-]){1,15}$",
- "description": "Tranport Document Number",
- "validationMsg": "Transport Receipt No is invalid"
- },
- "TransDocDt": {
- "type": "string",
- "minLength": 10,
- "maxLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
- "description": "Transport Document Date"
- },
- "VehNo": {
- "type": "string",
- "minLength": 4,
- "maxLength": 20,
- "description": "Vehicle Number"
- },
- "VehType": {
- "type": "string",
- "minLength": 1,
- "maxLength": 1,
- "enum": ["O", "R"],
- "description": "Vehicle Type"
- }
- },
- "required": ["Distance"]
- },
- "required": [
- "Version",
- "TranDtls",
- "DocDtls",
- "SellerDtls",
- "BuyerDtls",
- "ItemList",
- "ValDtls"
- ]
-}
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
deleted file mode 100644
index 8ad30fa..0000000
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ /dev/null
@@ -1,292 +0,0 @@
-erpnext.setup_einvoice_actions = (doctype) => {
- frappe.ui.form.on(doctype, {
- async refresh(frm) {
- if (frm.doc.docstatus == 2) return;
-
- const res = await frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility',
- args: { doc: frm.doc }
- });
- const invoice_eligible = res.message;
-
- if (!invoice_eligible) return;
-
- const { doctype, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, __unsaved } = frm.doc;
-
- const add_custom_button = (label, action) => {
- if (!frm.custom_buttons[label]) {
- frm.add_custom_button(label, action, __('E Invoicing'));
- }
- };
-
- if (!irn && !__unsaved) {
- const action = () => {
- if (frm.doc.__unsaved) {
- frappe.throw(__('Please save the document to generate IRN.'));
- }
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.get_einvoice',
- args: { doctype, docname: name },
- freeze: true,
- callback: (res) => {
- const einvoice = res.message;
- show_einvoice_preview(frm, einvoice);
- }
- });
- };
-
- add_custom_button(__("Generate IRN"), action);
- }
-
- if (irn && !irn_cancelled && !ewaybill) {
- const fields = [
- {
- "label": "Reason",
- "fieldname": "reason",
- "fieldtype": "Select",
- "reqd": 1,
- "default": "1-Duplicate",
- "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
- },
- {
- "label": "Remark",
- "fieldname": "remark",
- "fieldtype": "Data",
- "reqd": 1
- }
- ];
- const action = () => {
- const d = new frappe.ui.Dialog({
- title: __("Cancel IRN"),
- fields: fields,
- primary_action: function() {
- const data = d.get_values();
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.cancel_irn',
- args: {
- doctype,
- docname: name,
- irn: irn,
- reason: data.reason.split('-')[0],
- remark: data.remark
- },
- freeze: true,
- callback: () => frm.reload_doc() || d.hide(),
- error: () => d.hide()
- });
- },
- primary_action_label: __('Submit')
- });
- d.show();
- };
- add_custom_button(__("Cancel IRN"), action);
- }
-
- if (irn && !irn_cancelled && !ewaybill) {
- const action = () => {
- const d = new frappe.ui.Dialog({
- title: __('Generate E-Way Bill'),
- size: "large",
- fields: get_ewaybill_fields(frm),
- primary_action: function() {
- const data = d.get_values();
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.generate_eway_bill',
- args: {
- doctype,
- docname: name,
- irn,
- ...data
- },
- freeze: true,
- callback: () => frm.reload_doc() || d.hide(),
- error: () => d.hide()
- });
- },
- primary_action_label: __('Submit')
- });
- d.show();
- };
-
- add_custom_button(__("Generate E-Way Bill"), action);
- }
-
- if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
- const action = () => {
- let message = __('Cancellation of e-way bill is currently not supported.') + ' ';
- message += '<br><br>';
- message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.');
-
- const dialog = frappe.msgprint({
- title: __('Update E-Way Bill Cancelled Status?'),
- message: message,
- indicator: 'orange',
- primary_action: {
- action: function() {
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.cancel_eway_bill',
- args: { doctype, docname: name },
- freeze: true,
- callback: () => frm.reload_doc() || dialog.hide()
- });
- }
- },
- primary_action_label: __('Yes')
- });
- };
- add_custom_button(__("Cancel E-Way Bill"), action);
- }
- }
- });
-};
-
-const get_ewaybill_fields = (frm) => {
- return [
- {
- 'fieldname': 'transporter',
- 'label': 'Transporter',
- 'fieldtype': 'Link',
- 'options': 'Supplier',
- 'default': frm.doc.transporter
- },
- {
- 'fieldname': 'gst_transporter_id',
- 'label': 'GST Transporter ID',
- 'fieldtype': 'Data',
- 'fetch_from': 'transporter.gst_transporter_id',
- 'default': frm.doc.gst_transporter_id
- },
- {
- 'fieldname': 'driver',
- 'label': 'Driver',
- 'fieldtype': 'Link',
- 'options': 'Driver',
- 'default': frm.doc.driver
- },
- {
- 'fieldname': 'lr_no',
- 'label': 'Transport Receipt No',
- 'fieldtype': 'Data',
- 'default': frm.doc.lr_no
- },
- {
- 'fieldname': 'vehicle_no',
- 'label': 'Vehicle No',
- 'fieldtype': 'Data',
- 'default': frm.doc.vehicle_no
- },
- {
- 'fieldname': 'distance',
- 'label': 'Distance (in km)',
- 'fieldtype': 'Float',
- 'default': frm.doc.distance
- },
- {
- 'fieldname': 'transporter_col_break',
- 'fieldtype': 'Column Break',
- },
- {
- 'fieldname': 'transporter_name',
- 'label': 'Transporter Name',
- 'fieldtype': 'Data',
- 'fetch_from': 'transporter.name',
- 'read_only': 1,
- 'default': frm.doc.transporter_name
- },
- {
- 'fieldname': 'mode_of_transport',
- 'label': 'Mode of Transport',
- 'fieldtype': 'Select',
- 'options': `\nRoad\nAir\nRail\nShip`,
- 'default': frm.doc.mode_of_transport
- },
- {
- 'fieldname': 'driver_name',
- 'label': 'Driver Name',
- 'fieldtype': 'Data',
- 'fetch_from': 'driver.full_name',
- 'read_only': 1,
- 'default': frm.doc.driver_name
- },
- {
- 'fieldname': 'lr_date',
- 'label': 'Transport Receipt Date',
- 'fieldtype': 'Date',
- 'default': frm.doc.lr_date
- },
- {
- 'fieldname': 'gst_vehicle_type',
- 'label': 'GST Vehicle Type',
- 'fieldtype': 'Select',
- 'options': `Regular\nOver Dimensional Cargo (ODC)`,
- 'depends_on': 'eval:(doc.mode_of_transport === "Road")',
- 'default': frm.doc.gst_vehicle_type
- }
- ];
-};
-
-const request_irn_generation = (frm) => {
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.generate_irn',
- args: { doctype: frm.doc.doctype, docname: frm.doc.name },
- freeze: true,
- callback: () => frm.reload_doc()
- });
-};
-
-const get_preview_dialog = (frm, action) => {
- const dialog = new frappe.ui.Dialog({
- title: __("Preview"),
- size: "large",
- fields: [
- {
- "label": "Preview",
- "fieldname": "preview_html",
- "fieldtype": "HTML"
- }
- ],
- primary_action: () => action(frm) || dialog.hide(),
- primary_action_label: __('Generate IRN')
- });
- return dialog;
-};
-
-const show_einvoice_preview = (frm, einvoice) => {
- const preview_dialog = get_preview_dialog(frm, request_irn_generation);
-
- // initialize e-invoice fields
- einvoice["Irn"] = einvoice["AckNo"] = ''; einvoice["AckDt"] = frappe.datetime.nowdate();
- frm.doc.signed_einvoice = JSON.stringify(einvoice);
-
- // initialize preview wrapper
- const $preview_wrapper = preview_dialog.get_field("preview_html").$wrapper;
- $preview_wrapper.html(
- `<div>
- <div class="print-preview">
- <div class="print-format"></div>
- </div>
- <div class="page-break-message text-muted text-center text-medium margin-top"></div>
- </div>`
- );
-
- frappe.call({
- method: "frappe.www.printview.get_html_and_style",
- args: {
- doc: frm.doc,
- print_format: "GST E-Invoice",
- no_letterhead: 1
- },
- callback: function (r) {
- if (!r.exc) {
- $preview_wrapper.find(".print-format").html(r.message.html);
- const style = `
- .print-format { box-shadow: 0px 0px 5px rgba(0,0,0,0.2); padding: 0.30in; min-height: 80vh; }
- .print-preview { min-height: 0px; }
- .modal-dialog { width: 720px; }`;
-
- frappe.dom.set_style(style, "custom-print-style");
- preview_dialog.show();
- }
- }
- });
-};
\ No newline at end of file
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
deleted file mode 100644
index fa7e88d..0000000
--- a/erpnext/regional/india/e_invoice/utils.py
+++ /dev/null
@@ -1,1131 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import os
-import re
-import jwt
-import sys
-import json
-import base64
-import frappe
-import six
-import traceback
-import io
-from frappe import _, bold
-from pyqrcode import create as qrcreate
-from frappe.utils.background_jobs import enqueue
-from frappe.utils.scheduler import is_scheduler_inactive
-from frappe.core.page.background_jobs.background_jobs import get_info
-from frappe.integrations.utils import make_post_request, make_get_request
-from erpnext.regional.india.utils import get_gst_accounts, get_place_of_supply
-from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date, get_link_to_form, getdate, time_diff_in_hours
-
-@frappe.whitelist()
-def validate_eligibility(doc):
- if isinstance(doc, six.string_types):
- doc = json.loads(doc)
-
- invalid_doctype = doc.get('doctype') != 'Sales Invoice'
- if invalid_doctype:
- return False
-
- einvoicing_enabled = cint(frappe.db.get_single_value('E Invoice Settings', 'enable'))
- if not einvoicing_enabled:
- return False
-
- einvoicing_eligible_from = frappe.db.get_single_value('E Invoice Settings', 'applicable_from') or '2021-04-01'
- if getdate(doc.get('posting_date')) < getdate(einvoicing_eligible_from):
- return False
-
- invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') })
- invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
- company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
-
- # if export invoice, then taxes can be empty
- # invoice can only be ineligible if no taxes applied and is not an export invoice
- no_taxes_applied = not doc.get('taxes') and not doc.get('gst_category') == 'Overseas'
- has_non_gst_item = any(d for d in doc.get('items', []) if d.get('is_non_gst'))
-
- if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
- return False
-
- return True
-
-def validate_einvoice_fields(doc):
- invoice_eligible = validate_eligibility(doc)
-
- if not invoice_eligible:
- return
-
- if doc.docstatus == 0 and doc._action == 'save':
- if doc.irn:
- frappe.throw(_('You cannot edit the invoice after generating IRN'), title=_('Edit Not Allowed'))
- if len(doc.name) > 16:
- raise_document_name_too_long_error()
-
- doc.einvoice_status = 'Pending'
-
- elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
- frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
-
- elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
- frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed'))
-
-def raise_document_name_too_long_error():
- title = _('Document ID Too Long')
- msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice')
- msg += ', '
- msg += _('document id {} exceed 16 letters.').format(bold(_('should not')))
- msg += '<br><br>'
- msg += _('You must {} your {} in order to have document id of {} length 16.').format(
- bold(_('modify')), bold(_('naming series')), bold(_('maximum'))
- )
- msg += _('Please account for ammended documents too.')
- frappe.throw(msg, title=title)
-
-def read_json(name):
- file_path = os.path.join(os.path.dirname(__file__), '{name}.json'.format(name=name))
- with open(file_path, 'r') as f:
- return cstr(f.read())
-
-def get_transaction_details(invoice):
- supply_type = ''
- if invoice.gst_category == 'Registered Regular': supply_type = 'B2B'
- elif invoice.gst_category == 'SEZ': supply_type = 'SEZWOP'
- elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
- elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
-
- if not supply_type:
- rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
- frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
- title=_('Invalid Supply Type'))
-
- return frappe._dict(dict(
- tax_scheme='GST',
- supply_type=supply_type,
- reverse_charge=invoice.reverse_charge
- ))
-
-def get_doc_details(invoice):
- if getdate(invoice.posting_date) < getdate('2021-01-01'):
- frappe.throw(_('IRN generation is not allowed for invoices dated before 1st Jan 2021'), title=_('Not Allowed'))
-
- invoice_type = 'CRN' if invoice.is_return else 'INV'
-
- invoice_name = invoice.name
- invoice_date = format_date(invoice.posting_date, 'dd/mm/yyyy')
-
- return frappe._dict(dict(
- invoice_type=invoice_type,
- invoice_name=invoice_name,
- invoice_date=invoice_date
- ))
-
-def validate_address_fields(address, is_shipping_address):
- if ((not address.gstin and not is_shipping_address)
- or not address.city
- or not address.pincode
- or not address.address_title
- or not address.address_line1
- or not address.gst_state_number):
-
- frappe.throw(
- msg=_('Address Lines, City, Pincode, GSTIN are mandatory for address {}. Please set them and try again.').format(address.name),
- title=_('Missing Address Fields')
- )
-
-def get_party_details(address_name, is_shipping_address=False):
- addr = frappe.get_doc('Address', address_name)
-
- validate_address_fields(addr, is_shipping_address)
-
- if addr.gst_state_number == 97:
- # according to einvoice standard
- addr.pincode = 999999
-
- party_address_details = frappe._dict(dict(
- legal_name=sanitize_for_json(addr.address_title),
- location=sanitize_for_json(addr.city),
- pincode=addr.pincode, gstin=addr.gstin,
- state_code=addr.gst_state_number,
- address_line1=sanitize_for_json(addr.address_line1),
- address_line2=sanitize_for_json(addr.address_line2)
- ))
-
- return party_address_details
-
-def get_overseas_address_details(address_name):
- address_title, address_line1, address_line2, city = frappe.db.get_value(
- 'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city']
- )
-
- if not address_title or not address_line1 or not city:
- frappe.throw(
- msg=_('Address lines and city is mandatory for address {}. Please set them and try again.').format(
- get_link_to_form('Address', address_name)
- ),
- title=_('Missing Address Fields')
- )
-
- return frappe._dict(dict(
- gstin='URP',
- legal_name=sanitize_for_json(address_title),
- location=city,
- address_line1=sanitize_for_json(address_line1),
- address_line2=sanitize_for_json(address_line2),
- pincode=999999, state_code=96, place_of_supply=96
- ))
-
-def get_item_list(invoice):
- item_list = []
-
- for d in invoice.items:
- einvoice_item_schema = read_json('einv_item_template')
- item = frappe._dict({})
- item.update(d.as_dict())
-
- item.sr_no = d.idx
- item.description = sanitize_for_json(d.item_name)
-
- item.qty = abs(item.qty)
- if flt(item.qty) != 0.0:
- item.unit_rate = abs(item.taxable_value / item.qty)
- else:
- item.unit_rate = abs(item.taxable_value)
- item.gross_amount = abs(item.taxable_value)
- item.taxable_value = abs(item.taxable_value)
- item.discount_amount = 0
-
- item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
- item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
- item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N'
- item.serial_no = ""
-
- item = update_item_taxes(invoice, item)
-
- item.total_value = abs(
- item.taxable_value + item.igst_amount + item.sgst_amount +
- item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
- )
- einv_item = einvoice_item_schema.format(item=item)
- item_list.append(einv_item)
-
- return ', '.join(item_list)
-
-def update_item_taxes(invoice, item):
- gst_accounts = get_gst_accounts(invoice.company)
- gst_accounts_list = [d for accounts in gst_accounts.values() for d in accounts if d]
-
- for attr in [
- 'tax_rate', 'cess_rate', 'cess_nadv_amount',
- 'cgst_amount', 'sgst_amount', 'igst_amount',
- 'cess_amount', 'cess_nadv_amount', 'other_charges'
- ]:
- item[attr] = 0
-
- for t in invoice.taxes:
- is_applicable = t.tax_amount and t.account_head in gst_accounts_list
- if is_applicable:
- # this contains item wise tax rate & tax amount (incl. discount)
- item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code or item.item_name)
-
- item_tax_rate = item_tax_detail[0]
- # item tax amount excluding discount amount
- item_tax_amount = (item_tax_rate / 100) * item.taxable_value
-
- if t.account_head in gst_accounts.cess_account:
- item_tax_amount_after_discount = item_tax_detail[1]
- if t.charge_type == 'On Item Quantity':
- item.cess_nadv_amount += abs(item_tax_amount_after_discount)
- else:
- item.cess_rate += item_tax_rate
- item.cess_amount += abs(item_tax_amount_after_discount)
-
- for tax_type in ['igst', 'cgst', 'sgst']:
- if t.account_head in gst_accounts[f'{tax_type}_account']:
- item.tax_rate += item_tax_rate
- item[f'{tax_type}_amount'] += abs(item_tax_amount)
- else:
- # TODO: other charges per item
- pass
-
- return item
-
-def get_invoice_value_details(invoice):
- invoice_value_details = frappe._dict(dict())
- invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
- invoice_value_details.invoice_discount_amt = 0
-
- invoice_value_details.round_off = invoice.base_rounding_adjustment
- invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
- invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
-
- invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
-
- return invoice_value_details
-
-def update_invoice_taxes(invoice, invoice_value_details):
- gst_accounts = get_gst_accounts(invoice.company)
- gst_accounts_list = [d for accounts in gst_accounts.values() for d in accounts if d]
-
- invoice_value_details.total_cgst_amt = 0
- invoice_value_details.total_sgst_amt = 0
- invoice_value_details.total_igst_amt = 0
- invoice_value_details.total_cess_amt = 0
- invoice_value_details.total_other_charges = 0
- considered_rows = []
-
- for t in invoice.taxes:
- tax_amount = t.base_tax_amount_after_discount_amount
- if t.account_head in gst_accounts_list:
- if t.account_head in gst_accounts.cess_account:
- # using after discount amt since item also uses after discount amt for cess calc
- invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
-
- for tax_type in ['igst', 'cgst', 'sgst']:
- if t.account_head in gst_accounts[f'{tax_type}_account']:
-
- invoice_value_details[f'total_{tax_type}_amt'] += abs(tax_amount)
- update_other_charges(t, invoice_value_details, gst_accounts_list, invoice, considered_rows)
- else:
- invoice_value_details.total_other_charges += abs(tax_amount)
-
- return invoice_value_details
-
-def update_other_charges(tax_row, invoice_value_details, gst_accounts_list, invoice, considered_rows):
- prev_row_id = cint(tax_row.row_id) - 1
- if tax_row.account_head in gst_accounts_list and prev_row_id not in considered_rows:
- if tax_row.charge_type == 'On Previous Row Amount':
- amount = invoice.get('taxes')[prev_row_id].tax_amount_after_discount_amount
- invoice_value_details.total_other_charges -= abs(amount)
- considered_rows.append(prev_row_id)
- if tax_row.charge_type == 'On Previous Row Total':
- amount = invoice.get('taxes')[prev_row_id].base_total - invoice.base_net_total
- invoice_value_details.total_other_charges -= abs(amount)
- considered_rows.append(prev_row_id)
-
-def get_payment_details(invoice):
- payee_name = invoice.company
- mode_of_payment = ', '.join([d.mode_of_payment for d in invoice.payments])
- paid_amount = invoice.base_paid_amount
- outstanding_amount = invoice.outstanding_amount
-
- return frappe._dict(dict(
- payee_name=payee_name, mode_of_payment=mode_of_payment,
- paid_amount=paid_amount, outstanding_amount=outstanding_amount
- ))
-
-def get_return_doc_reference(invoice):
- invoice_date = frappe.db.get_value('Sales Invoice', invoice.return_against, 'posting_date')
- return frappe._dict(dict(
- invoice_name=invoice.return_against, invoice_date=format_date(invoice_date, 'dd/mm/yyyy')
- ))
-
-def get_eway_bill_details(invoice):
- if invoice.is_return:
- frappe.throw(_('E-Way Bill cannot be generated for Credit Notes & Debit Notes. Please clear fields in the Transporter Section of the invoice.'),
- title=_('Invalid Fields'))
-
-
- mode_of_transport = { '': '', 'Road': '1', 'Air': '2', 'Rail': '3', 'Ship': '4' }
- vehicle_type = { 'Regular': 'R', 'Over Dimensional Cargo (ODC)': 'O' }
-
- return frappe._dict(dict(
- gstin=invoice.gst_transporter_id,
- name=invoice.transporter_name,
- mode_of_transport=mode_of_transport[invoice.mode_of_transport],
- distance=invoice.distance or 0,
- document_name=invoice.lr_no,
- document_date=format_date(invoice.lr_date, 'dd/mm/yyyy'),
- vehicle_no=invoice.vehicle_no,
- vehicle_type=vehicle_type[invoice.gst_vehicle_type]
- ))
-
-def validate_mandatory_fields(invoice):
- if not invoice.company_address:
- frappe.throw(
- _('Company Address is mandatory to fetch company GSTIN details. Please set Company Address and try again.'),
- title=_('Missing Fields')
- )
- if not invoice.customer_address:
- frappe.throw(
- _('Customer Address is mandatory to fetch customer GSTIN details. Please set Company Address and try again.'),
- title=_('Missing Fields')
- )
- if not frappe.db.get_value('Address', invoice.company_address, 'gstin'):
- frappe.throw(
- _('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
- title=_('Missing Fields')
- )
- if invoice.gst_category != 'Overseas' and not frappe.db.get_value('Address', invoice.customer_address, 'gstin'):
- frappe.throw(
- _('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
- title=_('Missing Fields')
- )
-
-def validate_totals(einvoice):
- item_list = einvoice['ItemList']
- value_details = einvoice['ValDtls']
-
- total_item_ass_value = 0
- total_item_cgst_value = 0
- total_item_sgst_value = 0
- total_item_igst_value = 0
- total_item_value = 0
- for item in item_list:
- total_item_ass_value += flt(item['AssAmt'])
- total_item_cgst_value += flt(item['CgstAmt'])
- total_item_sgst_value += flt(item['SgstAmt'])
- total_item_igst_value += flt(item['IgstAmt'])
- total_item_value += flt(item['TotItemVal'])
-
- if abs(flt(item['AssAmt']) * flt(item['GstRt']) / 100) - (flt(item['CgstAmt']) + flt(item['SgstAmt']) + flt(item['IgstAmt'])) > 1:
- frappe.throw(_('Row #{}: GST rate is invalid. Please remove tax rows with zero tax amount from taxes table.').format(item.idx))
-
- if abs(flt(value_details['AssVal']) - total_item_ass_value) > 1:
- frappe.throw(_('Total Taxable Value of the items is not equal to the Invoice Net Total. Please check item taxes / discounts for any correction.'))
-
- if abs(
- flt(value_details['TotInvVal']) + flt(value_details['Discount']) -
- flt(value_details['OthChrg']) - flt(value_details['RndOffAmt']) -
- total_item_value) > 1:
- frappe.throw(_('Total Value of the items is not equal to the Invoice Grand Total. Please check item taxes / discounts for any correction.'))
-
- calculated_invoice_value = \
- flt(value_details['AssVal']) + flt(value_details['CgstVal']) \
- + flt(value_details['SgstVal']) + flt(value_details['IgstVal']) \
- + flt(value_details['OthChrg']) + flt(value_details['RndOffAmt']) - flt(value_details['Discount'])
-
- if abs(flt(value_details['TotInvVal']) - calculated_invoice_value) > 1:
- frappe.throw(_('Total Item Value + Taxes - Discount is not equal to the Invoice Grand Total. Please check taxes / discounts for any correction.'))
-
-def make_einvoice(invoice):
- validate_mandatory_fields(invoice)
-
- schema = read_json('einv_template')
-
- transaction_details = get_transaction_details(invoice)
- item_list = get_item_list(invoice)
- doc_details = get_doc_details(invoice)
- invoice_value_details = get_invoice_value_details(invoice)
- seller_details = get_party_details(invoice.company_address)
-
- if invoice.gst_category == 'Overseas':
- buyer_details = get_overseas_address_details(invoice.customer_address)
- else:
- buyer_details = get_party_details(invoice.customer_address)
- place_of_supply = get_place_of_supply(invoice, invoice.doctype)
- if place_of_supply:
- place_of_supply = place_of_supply.split('-')[0]
- else:
- place_of_supply = sanitize_for_json(invoice.billing_address_gstin)[:2]
- buyer_details.update(dict(place_of_supply=place_of_supply))
-
- seller_details.update(dict(legal_name=invoice.company))
- buyer_details.update(dict(legal_name=invoice.customer_name or invoice.customer))
-
- shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
- if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
- if invoice.gst_category == 'Overseas':
- shipping_details = get_overseas_address_details(invoice.shipping_address_name)
- else:
- shipping_details = get_party_details(invoice.shipping_address_name, is_shipping_address=True)
-
- if invoice.is_pos and invoice.base_paid_amount:
- payment_details = get_payment_details(invoice)
-
- if invoice.is_return and invoice.return_against:
- prev_doc_details = get_return_doc_reference(invoice)
-
- if invoice.transporter and not invoice.is_return:
- eway_bill_details = get_eway_bill_details(invoice)
-
- # not yet implemented
- dispatch_details = period_details = export_details = frappe._dict({})
-
- einvoice = schema.format(
- transaction_details=transaction_details, doc_details=doc_details, dispatch_details=dispatch_details,
- seller_details=seller_details, buyer_details=buyer_details, shipping_details=shipping_details,
- item_list=item_list, invoice_value_details=invoice_value_details, payment_details=payment_details,
- period_details=period_details, prev_doc_details=prev_doc_details,
- export_details=export_details, eway_bill_details=eway_bill_details
- )
-
- try:
- einvoice = safe_json_load(einvoice)
- einvoice = santize_einvoice_fields(einvoice)
- except Exception:
- show_link_to_error_log(invoice, einvoice)
-
- validate_totals(einvoice)
-
- return einvoice
-
-def show_link_to_error_log(invoice, einvoice):
- err_log = log_error(einvoice)
- link_to_error_log = get_link_to_form('Error Log', err_log.name, 'Error Log')
- frappe.throw(
- _('An error occurred while creating e-invoice for {}. Please check {} for more information.').format(
- invoice.name, link_to_error_log),
- title=_('E Invoice Creation Failed')
- )
-
-def log_error(data=None):
- if isinstance(data, six.string_types):
- data = json.loads(data)
-
- seperator = "--" * 50
- err_tb = traceback.format_exc()
- err_msg = str(sys.exc_info()[1])
- data = json.dumps(data, indent=4)
-
- message = "\n".join([
- "Error", err_msg, seperator,
- "Data:", data, seperator,
- "Exception:", err_tb
- ])
- frappe.log_error(title=_('E Invoice Request Failed'), message=message)
-
-def santize_einvoice_fields(einvoice):
- int_fields = ["Pin","Distance","CrDay"]
- float_fields = ["Qty","FreeQty","UnitPrice","TotAmt","Discount","PreTaxVal","AssAmt","GstRt","IgstAmt","CgstAmt","SgstAmt","CesRt","CesAmt","CesNonAdvlAmt","StateCesRt","StateCesAmt","StateCesNonAdvlAmt","OthChrg","TotItemVal","AssVal","CgstVal","SgstVal","IgstVal","CesVal","StCesVal","Discount","OthChrg","RndOffAmt","TotInvVal","TotInvValFc","PaidAmt","PaymtDue","ExpDuty",]
- copy = einvoice.copy()
- for key, value in copy.items():
- if isinstance(value, list):
- for idx, d in enumerate(value):
- santized_dict = santize_einvoice_fields(d)
- if santized_dict:
- einvoice[key][idx] = santized_dict
- else:
- einvoice[key].pop(idx)
-
- if not einvoice[key]:
- einvoice.pop(key, None)
-
- elif isinstance(value, dict):
- santized_dict = santize_einvoice_fields(value)
- if santized_dict:
- einvoice[key] = santized_dict
- else:
- einvoice.pop(key, None)
-
- elif not value or value == "None":
- einvoice.pop(key, None)
-
- elif key in float_fields:
- einvoice[key] = flt(value, 2)
-
- elif key in int_fields:
- einvoice[key] = cint(value)
-
- return einvoice
-
-def safe_json_load(json_string):
- try:
- return json.loads(json_string)
- except json.JSONDecodeError as e:
- # print a snippet of 40 characters around the location where error occured
- pos = e.pos
- start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
- snippet = json_string[start:end]
- frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
-
-class RequestFailed(Exception):
- pass
-class CancellationNotAllowed(Exception):
- pass
-
-class GSPConnector():
- def __init__(self, doctype=None, docname=None):
- self.doctype = doctype
- self.docname = docname
-
- self.set_invoice()
- self.set_credentials()
-
- # authenticate url is same for sandbox & live
- self.authenticate_url = 'https://gsp.adaequare.com/gsp/authenticate?grant_type=token'
- self.base_url = 'https://gsp.adaequare.com' if not self.e_invoice_settings.sandbox_mode else 'https://gsp.adaequare.com/test'
-
- self.cancel_irn_url = self.base_url + '/enriched/ei/api/invoice/cancel'
- self.irn_details_url = self.base_url + '/enriched/ei/api/invoice/irn'
- self.generate_irn_url = self.base_url + '/enriched/ei/api/invoice'
- self.gstin_details_url = self.base_url + '/enriched/ei/api/master/gstin'
- self.cancel_ewaybill_url = self.base_url + '/enriched/ewb/ewayapi?action=CANEWB'
- self.generate_ewaybill_url = self.base_url + '/enriched/ei/api/ewaybill'
-
- def set_invoice(self):
- self.invoice = None
- if self.doctype and self.docname:
- self.invoice = frappe.get_cached_doc(self.doctype, self.docname)
-
- def set_credentials(self):
- self.e_invoice_settings = frappe.get_cached_doc('E Invoice Settings')
-
- if not self.e_invoice_settings.enable:
- frappe.throw(_("E-Invoicing is disabled. Please enable it from {} to generate e-invoices.").format(get_link_to_form("E Invoice Settings", "E Invoice Settings")))
-
- if self.invoice:
- gstin = self.get_seller_gstin()
- credentials_for_gstin = [d for d in self.e_invoice_settings.credentials if d.gstin == gstin]
- if credentials_for_gstin:
- self.credentials = credentials_for_gstin[0]
- else:
- frappe.throw(_('Cannot find e-invoicing credentials for selected Company GSTIN. Please check E-Invoice Settings'))
- else:
- self.credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
-
- def get_seller_gstin(self):
- gstin = frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
- if not gstin:
- frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
- return gstin
-
- def get_auth_token(self):
- if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0:
- self.fetch_auth_token()
-
- return self.e_invoice_settings.auth_token
-
- def make_request(self, request_type, url, headers=None, data=None):
- if request_type == 'post':
- res = make_post_request(url, headers=headers, data=data)
- else:
- res = make_get_request(url, headers=headers, data=data)
-
- self.log_request(url, headers, data, res)
- return res
-
- def log_request(self, url, headers, data, res):
- headers.update({ 'password': self.credentials.password })
- request_log = frappe.get_doc({
- "doctype": "E Invoice Request Log",
- "user": frappe.session.user,
- "reference_invoice": self.invoice.name if self.invoice else None,
- "url": url,
- "headers": json.dumps(headers, indent=4) if headers else None,
- "data": json.dumps(data, indent=4) if isinstance(data, dict) else data,
- "response": json.dumps(res, indent=4) if res else None
- })
- request_log.save(ignore_permissions=True)
- frappe.db.commit()
-
- def fetch_auth_token(self):
- headers = {
- 'gspappid': frappe.conf.einvoice_client_id,
- 'gspappsecret': frappe.conf.einvoice_client_secret
- }
- res = {}
- try:
- res = self.make_request('post', self.authenticate_url, headers)
- self.e_invoice_settings.auth_token = "{} {}".format(res.get('token_type'), res.get('access_token'))
- self.e_invoice_settings.token_expiry = add_to_date(None, seconds=res.get('expires_in'))
- self.e_invoice_settings.save(ignore_permissions=True)
- self.e_invoice_settings.reload()
-
- except Exception:
- log_error(res)
- self.raise_error(True)
-
- def get_headers(self):
- return {
- 'content-type': 'application/json',
- 'user_name': self.credentials.username,
- 'password': self.credentials.get_password(),
- 'gstin': self.credentials.gstin,
- 'authorization': self.get_auth_token(),
- 'requestid': str(base64.b64encode(os.urandom(18))),
- }
-
- def fetch_gstin_details(self, gstin):
- headers = self.get_headers()
-
- try:
- params = '?gstin={gstin}'.format(gstin=gstin)
- res = self.make_request('get', self.gstin_details_url + params, headers)
- if res.get('success'):
- return res.get('result')
- else:
- log_error(res)
- raise RequestFailed
-
- except RequestFailed:
- self.raise_error()
-
- except Exception:
- log_error()
- self.raise_error(True)
- @staticmethod
- def get_gstin_details(gstin):
- '''fetch and cache GSTIN details'''
- if not hasattr(frappe.local, 'gstin_cache'):
- frappe.local.gstin_cache = {}
-
- key = gstin
- gsp_connector = GSPConnector()
- details = gsp_connector.fetch_gstin_details(gstin)
-
- frappe.local.gstin_cache[key] = details
- frappe.cache().hset('gstin_cache', key, details)
- return details
-
- def generate_irn(self):
- data = {}
- try:
- headers = self.get_headers()
- einvoice = make_einvoice(self.invoice)
- data = json.dumps(einvoice, indent=4)
- res = self.make_request('post', self.generate_irn_url, headers, data)
-
- if res.get('success'):
- self.set_einvoice_data(res.get('result'))
-
- elif '2150' in res.get('message'):
- # IRN already generated but not updated in invoice
- # Extract the IRN from the response description and fetch irn details
- irn = res.get('result')[0].get('Desc').get('Irn')
- irn_details = self.get_irn_details(irn)
- if irn_details:
- self.set_einvoice_data(irn_details)
- else:
- raise RequestFailed('IRN has already been generated for the invoice but cannot fetch details for the it. \
- Contact ERPNext support to resolve the issue.')
-
- else:
- raise RequestFailed
-
- except RequestFailed:
- errors = self.sanitize_error_message(res.get('message'))
- self.set_failed_status(errors=errors)
- self.raise_error(errors=errors)
-
- except Exception as e:
- self.set_failed_status(errors=str(e))
- log_error(data)
- self.raise_error(True)
-
- @staticmethod
- def bulk_generate_irn(invoices):
- gsp_connector = GSPConnector()
- gsp_connector.doctype = 'Sales Invoice'
-
- failed = []
-
- for invoice in invoices:
- try:
- gsp_connector.docname = invoice
- gsp_connector.set_invoice()
- gsp_connector.set_credentials()
- gsp_connector.generate_irn()
-
- except Exception as e:
- failed.append({
- 'docname': invoice,
- 'message': str(e)
- })
-
- return failed
-
- def get_irn_details(self, irn):
- headers = self.get_headers()
-
- try:
- params = '?irn={irn}'.format(irn=irn)
- res = self.make_request('get', self.irn_details_url + params, headers)
- if res.get('success'):
- return res.get('result')
- else:
- raise RequestFailed
-
- except RequestFailed:
- errors = self.sanitize_error_message(res.get('message'))
- self.raise_error(errors=errors)
-
- except Exception:
- log_error()
- self.raise_error(True)
-
- def cancel_irn(self, irn, reason, remark):
- data, res = {}, {}
- try:
- # validate cancellation
- if time_diff_in_hours(now_datetime(), self.invoice.ack_date) > 24:
- frappe.throw(_('E-Invoice cannot be cancelled after 24 hours of IRN generation.'), title=_('Not Allowed'), exc=CancellationNotAllowed)
- if not irn:
- frappe.throw(_('IRN not found. You must generate IRN before cancelling.'), title=_('Not Allowed'), exc=CancellationNotAllowed)
-
- headers = self.get_headers()
- data = json.dumps({
- 'Irn': irn,
- 'Cnlrsn': reason,
- 'Cnlrem': remark
- }, indent=4)
-
- res = self.make_request('post', self.cancel_irn_url, headers, data)
- if res.get('success') or '9999' in res.get('message'):
- self.invoice.irn_cancelled = 1
- self.invoice.irn_cancel_date = res.get('result')['CancelDate'] if res.get('result') else ""
- self.invoice.einvoice_status = 'Cancelled'
- self.invoice.flags.updater_reference = {
- 'doctype': self.invoice.doctype,
- 'docname': self.invoice.name,
- 'label': _('IRN Cancelled - {}').format(remark)
- }
- self.update_invoice()
-
- else:
- raise RequestFailed
-
- except RequestFailed:
- errors = self.sanitize_error_message(res.get('message'))
- self.set_failed_status(errors=errors)
- self.raise_error(errors=errors)
-
- except CancellationNotAllowed as e:
- self.set_failed_status(errors=str(e))
- self.raise_error(errors=str(e))
-
- except Exception as e:
- self.set_failed_status(errors=str(e))
- log_error(data)
- self.raise_error(True)
-
- @staticmethod
- def bulk_cancel_irn(invoices, reason, remark):
- gsp_connector = GSPConnector()
- gsp_connector.doctype = 'Sales Invoice'
-
- failed = []
-
- for invoice in invoices:
- try:
- gsp_connector.docname = invoice
- gsp_connector.set_invoice()
- gsp_connector.set_credentials()
- irn = gsp_connector.invoice.irn
- gsp_connector.cancel_irn(irn, reason, remark)
-
- except Exception as e:
- failed.append({
- 'docname': invoice,
- 'message': str(e)
- })
-
- return failed
-
- def generate_eway_bill(self, **kwargs):
- args = frappe._dict(kwargs)
-
- headers = self.get_headers()
- eway_bill_details = get_eway_bill_details(args)
- data = json.dumps({
- 'Irn': args.irn,
- 'Distance': cint(eway_bill_details.distance),
- 'TransMode': eway_bill_details.mode_of_transport,
- 'TransId': eway_bill_details.gstin,
- 'TransName': eway_bill_details.transporter,
- 'TrnDocDt': eway_bill_details.document_date,
- 'TrnDocNo': eway_bill_details.document_name,
- 'VehNo': eway_bill_details.vehicle_no,
- 'VehType': eway_bill_details.vehicle_type
- }, indent=4)
-
- try:
- res = self.make_request('post', self.generate_ewaybill_url, headers, data)
- if res.get('success'):
- self.invoice.ewaybill = res.get('result').get('EwbNo')
- self.invoice.eway_bill_validity = res.get('result').get('EwbValidTill')
- self.invoice.eway_bill_cancelled = 0
- self.invoice.update(args)
- self.invoice.flags.updater_reference = {
- 'doctype': self.invoice.doctype,
- 'docname': self.invoice.name,
- 'label': _('E-Way Bill Generated')
- }
- self.update_invoice()
-
- else:
- raise RequestFailed
-
- except RequestFailed:
- errors = self.sanitize_error_message(res.get('message'))
- self.raise_error(errors=errors)
-
- except Exception:
- log_error(data)
- self.raise_error(True)
-
- def cancel_eway_bill(self, eway_bill, reason, remark):
- headers = self.get_headers()
- data = json.dumps({
- 'ewbNo': eway_bill,
- 'cancelRsnCode': reason,
- 'cancelRmrk': remark
- }, indent=4)
- headers["username"] = headers["user_name"]
- del headers["user_name"]
- try:
- res = self.make_request('post', self.cancel_ewaybill_url, headers, data)
- if res.get('success'):
- self.invoice.ewaybill = ''
- self.invoice.eway_bill_cancelled = 1
- self.invoice.flags.updater_reference = {
- 'doctype': self.invoice.doctype,
- 'docname': self.invoice.name,
- 'label': _('E-Way Bill Cancelled - {}').format(remark)
- }
- self.update_invoice()
-
- else:
- raise RequestFailed
-
- except RequestFailed:
- errors = self.sanitize_error_message(res.get('message'))
- self.raise_error(errors=errors)
-
- except Exception:
- log_error(data)
- self.raise_error(True)
-
- def sanitize_error_message(self, message):
- '''
- On validation errors, response message looks something like this:
- message = '2174 : For inter-state transaction, CGST and SGST amounts are not applicable; only IGST amount is applicable,
- 3095 : Supplier GSTIN is inactive'
- we search for string between ':' to extract the error messages
- errors = [
- ': For inter-state transaction, CGST and SGST amounts are not applicable; only IGST amount is applicable, 3095 ',
- ': Test'
- ]
- then we trim down the message by looping over errors
- '''
- if not message:
- return []
-
- errors = re.findall(': [^:]+', message)
- for idx, e in enumerate(errors):
- # remove colons
- errors[idx] = errors[idx].replace(':', '').strip()
- # if not last
- if idx != len(errors) - 1:
- # remove last 7 chars eg: ', 3095 '
- errors[idx] = errors[idx][:-6]
-
- return errors
-
- def raise_error(self, raise_exception=False, errors=[]):
- title = _('E Invoice Request Failed')
- if errors:
- frappe.throw(errors, title=title, as_list=1)
- else:
- link_to_error_list = '<a href="desk#List/Error Log/List?method=E Invoice Request Failed">Error Log</a>'
- frappe.msgprint(
- _('An error occurred while making e-invoicing request. Please check {} for more information.').format(link_to_error_list),
- title=title,
- raise_exception=raise_exception,
- indicator='red'
- )
-
- def set_einvoice_data(self, res):
- enc_signed_invoice = res.get('SignedInvoice')
- dec_signed_invoice = jwt.decode(enc_signed_invoice, options={"verify_signature": False})['data']
-
- self.invoice.irn = res.get('Irn')
- self.invoice.ewaybill = res.get('EwbNo')
- self.invoice.eway_bill_validity = res.get('EwbValidTill')
- self.invoice.ack_no = res.get('AckNo')
- self.invoice.ack_date = res.get('AckDt')
- self.invoice.signed_einvoice = dec_signed_invoice
- self.invoice.ack_no = res.get('AckNo')
- self.invoice.ack_date = res.get('AckDt')
- self.invoice.signed_qr_code = res.get('SignedQRCode')
- self.invoice.einvoice_status = 'Generated'
-
- self.attach_qrcode_image()
-
- self.invoice.flags.updater_reference = {
- 'doctype': self.invoice.doctype,
- 'docname': self.invoice.name,
- 'label': _('IRN Generated')
- }
- self.update_invoice()
-
- def attach_qrcode_image(self):
- qrcode = self.invoice.signed_qr_code
- doctype = self.invoice.doctype
- docname = self.invoice.name
- filename = 'QRCode_{}.png'.format(docname).replace(os.path.sep, "__")
-
- qr_image = io.BytesIO()
- url = qrcreate(qrcode, error='L')
- url.png(qr_image, scale=2, quiet_zone=1)
- _file = frappe.get_doc({
- "doctype": "File",
- "file_name": filename,
- "attached_to_doctype": doctype,
- "attached_to_name": docname,
- "attached_to_field": "qrcode_image",
- "is_private": 0,
- "content": qr_image.getvalue()})
- _file.save()
- frappe.db.commit()
- self.invoice.qrcode_image = _file.file_url
-
- def update_invoice(self):
- self.invoice.flags.ignore_validate_update_after_submit = True
- self.invoice.flags.ignore_validate = True
- self.invoice.save()
-
- def set_failed_status(self, errors=None):
- frappe.db.rollback()
- self.invoice.einvoice_status = 'Failed'
- self.invoice.failure_description = self.get_failure_message(errors) if errors else ""
- self.update_invoice()
- frappe.db.commit()
-
- def get_failure_message(self, errors):
- if isinstance(errors, list):
- errors = ', '.join(errors)
- return errors
-
-def sanitize_for_json(string):
- """Escape JSON specific characters from a string."""
-
- # json.dumps adds double-quotes to the string. Indexing to remove them.
- return json.dumps(string)[1:-1]
-
-@frappe.whitelist()
-def get_einvoice(doctype, docname):
- invoice = frappe.get_doc(doctype, docname)
- return make_einvoice(invoice)
-
-@frappe.whitelist()
-def generate_irn(doctype, docname):
- gsp_connector = GSPConnector(doctype, docname)
- gsp_connector.generate_irn()
-
-@frappe.whitelist()
-def cancel_irn(doctype, docname, irn, reason, remark):
- gsp_connector = GSPConnector(doctype, docname)
- gsp_connector.cancel_irn(irn, reason, remark)
-
-@frappe.whitelist()
-def generate_eway_bill(doctype, docname, **kwargs):
- gsp_connector = GSPConnector(doctype, docname)
- gsp_connector.generate_eway_bill(**kwargs)
-
-@frappe.whitelist()
-def cancel_eway_bill(doctype, docname):
- # TODO: uncomment when eway_bill api from Adequare is enabled
- # gsp_connector = GSPConnector(doctype, docname)
- # gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
-
- frappe.db.set_value(doctype, docname, 'ewaybill', '')
- frappe.db.set_value(doctype, docname, 'eway_bill_cancelled', 1)
-
-@frappe.whitelist()
-def generate_einvoices(docnames):
- docnames = json.loads(docnames) or []
-
- if len(docnames) < 10:
- failures = GSPConnector.bulk_generate_irn(docnames)
- frappe.local.message_log = []
-
- if failures:
- show_bulk_action_failure_message(failures)
-
- success = len(docnames) - len(failures)
- frappe.msgprint(
- _('{} e-invoices generated successfully').format(success),
- title=_('Bulk E-Invoice Generation Complete')
- )
-
- else:
- enqueue_bulk_action(schedule_bulk_generate_irn, docnames=docnames)
-
-def schedule_bulk_generate_irn(docnames):
- failures = GSPConnector.bulk_generate_irn(docnames)
- frappe.local.message_log = []
-
- frappe.publish_realtime("bulk_einvoice_generation_complete", {
- "user": frappe.session.user,
- "failures": failures,
- "invoices": docnames
- })
-
-def show_bulk_action_failure_message(failures):
- for doc in failures:
- docname = '<a href="sales-invoice/{0}">{0}</a>'.format(doc.get('docname'))
- message = doc.get('message').replace("'", '"')
- if message[0] == '[':
- errors = json.loads(message)
- error_list = ''.join(['<li>{}</li>'.format(err) for err in errors])
- message = '''{} has following errors:<br>
- <ul style="padding-left: 20px; padding-top: 5px">{}</ul>'''.format(docname, error_list)
- else:
- message = '{} - {}'.format(docname, message)
-
- frappe.msgprint(
- message,
- title=_('Bulk E-Invoice Generation Complete'),
- indicator='red'
- )
-
-@frappe.whitelist()
-def cancel_irns(docnames, reason, remark):
- docnames = json.loads(docnames) or []
-
- if len(docnames) < 10:
- failures = GSPConnector.bulk_cancel_irn(docnames, reason, remark)
- frappe.local.message_log = []
-
- if failures:
- show_bulk_action_failure_message(failures)
-
- success = len(docnames) - len(failures)
- frappe.msgprint(
- _('{} e-invoices cancelled successfully').format(success),
- title=_('Bulk E-Invoice Cancellation Complete')
- )
- else:
- enqueue_bulk_action(schedule_bulk_cancel_irn, docnames=docnames, reason=reason, remark=remark)
-
-def schedule_bulk_cancel_irn(docnames, reason, remark):
- failures = GSPConnector.bulk_cancel_irn(docnames, reason, remark)
- frappe.local.message_log = []
-
- frappe.publish_realtime("bulk_einvoice_cancellation_complete", {
- "user": frappe.session.user,
- "failures": failures,
- "invoices": docnames
- })
-
-def enqueue_bulk_action(job, **kwargs):
- check_scheduler_status()
-
- enqueue(
- job,
- **kwargs,
- queue="long",
- timeout=10000,
- event="processing_bulk_einvoice_action",
- now=frappe.conf.developer_mode or frappe.flags.in_test,
- )
-
- if job == schedule_bulk_generate_irn:
- msg = _('E-Invoices will be generated in a background process.')
- else:
- msg = _('E-Invoices will be cancelled 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
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 2d6b913..afb1b07 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -61,7 +61,7 @@
def add_custom_roles_for_reports():
for report_name in ('GST Sales Register', 'GST Purchase Register',
- 'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill', 'E-Invoice Summary'):
+ 'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill'):
if not frappe.db.get_value('Custom Role', dict(report=report_name)):
frappe.get_doc(dict(
@@ -100,7 +100,7 @@
)).insert()
def add_permissions():
- for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate', 'E Invoice Settings'):
+ for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'):
add_permission(doctype, 'All', 0)
for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
add_permission(doctype, role, 0)
@@ -116,11 +116,9 @@
def add_print_formats():
frappe.reload_doc("regional", "print_format", "gst_tax_invoice")
frappe.reload_doc("accounts", "print_format", "gst_pos_invoice")
- frappe.reload_doc("accounts", "print_format", "GST E-Invoice")
frappe.db.set_value("Print Format", "GST POS Invoice", "disabled", 0)
frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0)
- frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0)
def make_property_setters(patch=False):
# GST rules do not allow for an invoice no. bigger than 16 characters
@@ -134,6 +132,10 @@
make_property_setter('Journal Entry', 'voucher_type', 'options', '\n'.join(journal_entry_types), '')
def make_custom_fields(update=True):
+ custom_fields = get_custom_fields()
+ create_custom_fields(custom_fields, update=update)
+
+def get_custom_fields():
hsn_sac_field = 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=1, fetch_if_empty=1)
@@ -167,12 +169,12 @@
dict(fieldname='gst_category', label='GST Category',
fieldtype='Select', insert_after='gst_section', print_hide=1,
options='\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
- fetch_from='customer.gst_category', fetch_if_empty=1),
+ fetch_from='customer.gst_category', fetch_if_empty=1, length=25),
dict(fieldname='export_type', label='Export Type',
fieldtype='Select', insert_after='gst_category', print_hide=1,
depends_on='eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
options='\nWith Payment of Tax\nWithout Payment of Tax', fetch_from='customer.export_type',
- fetch_if_empty=1),
+ fetch_if_empty=1, length=25),
]
delivery_note_gst_category = [
@@ -183,18 +185,18 @@
]
invoice_gst_fields = [
- dict(fieldname='invoice_copy', label='Invoice Copy',
+ dict(fieldname='invoice_copy', label='Invoice Copy', length=30,
fieldtype='Select', insert_after='export_type', print_hide=1, allow_on_submit=1,
options='Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier'),
- dict(fieldname='reverse_charge', label='Reverse Charge',
+ dict(fieldname='reverse_charge', label='Reverse Charge', length=2,
fieldtype='Select', insert_after='invoice_copy', print_hide=1,
options='Y\nN', default='N'),
- dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN',
+ dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN', length=15,
fieldtype='Data', insert_after='export_type', print_hide=1),
dict(fieldname='gst_col_break', fieldtype='Column Break', insert_after='ecommerce_gstin'),
dict(fieldname='reason_for_issuing_document', label='Reason For Issuing document',
fieldtype='Select', insert_after='gst_col_break', print_hide=1,
- depends_on='eval:doc.is_return==1',
+ depends_on='eval:doc.is_return==1', length=45,
options='\n01-Sales Return\n02-Post Sale Discount\n03-Deficiency in services\n04-Correction in Invoice\n05-Change in POS\n06-Finalization of Provisional assessment\n07-Others')
]
@@ -232,25 +234,25 @@
sales_invoice_gst_fields = [
dict(fieldname='billing_address_gstin', label='Billing Address GSTIN',
fieldtype='Data', insert_after='customer_address', read_only=1,
- fetch_from='customer_address.gstin', print_hide=1),
+ fetch_from='customer_address.gstin', print_hide=1, length=15),
dict(fieldname='customer_gstin', label='Customer GSTIN',
fieldtype='Data', insert_after='shipping_address_name',
- fetch_from='shipping_address_name.gstin', print_hide=1),
+ fetch_from='shipping_address_name.gstin', print_hide=1, length=15),
dict(fieldname='place_of_supply', label='Place of Supply',
fieldtype='Data', insert_after='customer_gstin',
- print_hide=1, read_only=1),
+ print_hide=1, read_only=1, length=50),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address',
- fetch_from='company_address.gstin', print_hide=1, read_only=1),
+ fetch_from='company_address.gstin', print_hide=1, read_only=1, length=15),
]
sales_invoice_shipping_fields = [
dict(fieldname='port_code', label='Port Code',
fieldtype='Data', insert_after='reason_for_issuing_document', print_hide=1,
- depends_on="eval:doc.gst_category=='Overseas' "),
+ depends_on="eval:doc.gst_category=='Overseas' ", length=15),
dict(fieldname='shipping_bill_number', label=' Shipping Bill Number',
fieldtype='Data', insert_after='port_code', print_hide=1,
- depends_on="eval:doc.gst_category=='Overseas' "),
+ depends_on="eval:doc.gst_category=='Overseas' ", length=50),
dict(fieldname='shipping_bill_date', label='Shipping Bill Date',
fieldtype='Date', insert_after='shipping_bill_number', print_hide=1,
depends_on="eval:doc.gst_category=='Overseas' "),
@@ -356,7 +358,8 @@
'insert_after': 'transporter',
'fetch_from': 'transporter.gst_transporter_id',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 20
},
{
'fieldname': 'driver',
@@ -372,7 +375,8 @@
'fieldtype': 'Data',
'insert_after': 'driver',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 30
},
{
'fieldname': 'vehicle_no',
@@ -380,7 +384,8 @@
'fieldtype': 'Data',
'insert_after': 'lr_no',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 10
},
{
'fieldname': 'distance',
@@ -397,7 +402,7 @@
{
'fieldname': 'transporter_name',
'label': 'Transporter Name',
- 'fieldtype': 'Data',
+ 'fieldtype': 'Small Text',
'insert_after': 'transporter_col_break',
'fetch_from': 'transporter.name',
'read_only': 1,
@@ -411,12 +416,13 @@
'options': '\nRoad\nAir\nRail\nShip',
'insert_after': 'transporter_name',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 5
},
{
'fieldname': 'driver_name',
'label': 'Driver Name',
- 'fieldtype': 'Data',
+ 'fieldtype': 'Small Text',
'insert_after': 'mode_of_transport',
'fetch_from': 'driver.full_name',
'print_hide': 1,
@@ -439,57 +445,39 @@
'default': 'Regular',
'insert_after': 'lr_date',
'print_hide': 1,
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 30
},
{
'fieldname': 'ewaybill',
'label': 'E-Way Bill No.',
'fieldtype': 'Data',
- 'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)',
+ 'depends_on': 'eval:(doc.docstatus === 1)',
'allow_on_submit': 1,
'insert_after': 'tax_id',
- 'translatable': 0
+ 'translatable': 0,
+ 'length': 20
}
]
- si_einvoice_fields = [
- dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1,
- depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
-
- dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
- depends_on='eval: doc.irn', allow_on_submit=1, insert_after='customer'),
-
- dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
- depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'),
-
- dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
- depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
-
- dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
- print_hide=1, hidden=1),
-
- dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
- no_copy=1, print_hide=1),
-
- dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
-
- dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date',
- no_copy=1, print_hide=1),
-
- dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
- no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
- no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
- no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
- options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
-
- dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
- hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
+ payment_entry_fields = [
+ dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break', insert_after='deductions',
+ print_hide=1, collapsible=1),
+ dict(fieldname='company_address', label='Company Address', fieldtype='Link', insert_after='gst_section',
+ print_hide=1, options='Address'),
+ dict(fieldname='company_gstin', label='Company GSTIN',
+ fieldtype='Data', insert_after='company_address',
+ fetch_from='company_address.gstin', print_hide=1, read_only=1),
+ dict(fieldname='place_of_supply', label='Place of Supply',
+ fieldtype='Data', insert_after='company_gstin',
+ print_hide=1, read_only=1),
+ dict(fieldname='gst_column_break', fieldtype='Column Break',
+ insert_after='place_of_supply'),
+ dict(fieldname='customer_address', label='Customer Address', fieldtype='Link', insert_after='gst_column_break',
+ print_hide=1, options='Address', depends_on = 'eval:doc.party_type == "Customer"'),
+ dict(fieldname='customer_gstin', label='Customer GSTIN',
+ fieldtype='Data', insert_after='customer_address',
+ fetch_from='customer_address.gstin', print_hide=1, read_only=1)
]
custom_fields = {
@@ -504,8 +492,10 @@
'Purchase Invoice': purchase_invoice_gst_category + invoice_gst_fields + purchase_invoice_itc_fields + purchase_invoice_gst_fields,
'Purchase Order': purchase_invoice_gst_fields,
'Purchase Receipt': purchase_invoice_gst_fields,
- 'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields,
+ 'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields,
+ 'POS Invoice': sales_invoice_gst_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category,
+ 'Payment Entry': payment_entry_fields,
'Journal Entry': journal_entry_fields,
'Sales Order': sales_invoice_gst_fields,
'Tax Category': inter_state_gst_field,
@@ -522,6 +512,7 @@
'Sales Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Delivery Note Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Sales Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
+ 'POS Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
'Purchase Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
@@ -573,6 +564,7 @@
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_component'),
dict(fieldname='non_profit_section', label='Non Profit Settings',
@@ -581,6 +573,7 @@
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='with_effect_from')
],
@@ -680,9 +673,19 @@
'fieldtype': 'Data',
'insert_after': 'email'
}
+ ],
+ '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%.'
+ }
]
}
- create_custom_fields(custom_fields, update=update)
+
+ return custom_fields
def make_fixtures(company=None):
docs = []
@@ -769,7 +772,7 @@
def set_tax_withholding_category(company):
accounts = []
- fiscal_year = None
+ fiscal_year_details = None
abbr = frappe.get_value("Company", company, "abbr")
tds_account = frappe.get_value("Account", 'TDS Payable - {0}'.format(abbr), 'name')
@@ -777,11 +780,11 @@
accounts = [dict(company=company, account=tds_account)]
try:
- fiscal_year = get_fiscal_year(today(), verbose=0, company=company)[0]
+ fiscal_year_details = get_fiscal_year(today(), verbose=0, company=company)
except FiscalYearError:
pass
- docs = get_tds_details(accounts, fiscal_year)
+ docs = get_tds_details(accounts, fiscal_year_details)
for d in docs:
if not frappe.db.exists("Tax Withholding Category", d.get("name")):
@@ -796,9 +799,10 @@
if accounts:
doc.append("accounts", accounts[0])
- if fiscal_year:
+ if fiscal_year_details:
# if fiscal year don't match with any of the already entered data, append rate row
- fy_exist = [k for k in doc.get('rates') if k.get('fiscal_year')==fiscal_year]
+ fy_exist = [k for k in doc.get('rates') if k.get('from_date') <= fiscal_year_details[1] \
+ and k.get('to_date') >= fiscal_year_details[2]]
if not fy_exist:
doc.append("rates", d.get('rates')[0])
@@ -821,149 +825,149 @@
}
])
-def get_tds_details(accounts, fiscal_year):
+def get_tds_details(accounts, fiscal_year_details):
# bootstrap default tax withholding sections
return [
dict(name="TDS - 194C - Company",
category_name="Payment to Contractors (Single / Aggregate)",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
- "single_threshold": 30000, "cumulative_threshold": 100000}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 2, "single_threshold": 30000, "cumulative_threshold": 100000}]),
dict(name="TDS - 194C - Individual",
category_name="Payment to Contractors (Single / Aggregate)",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
- "single_threshold": 30000, "cumulative_threshold": 100000}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 1, "single_threshold": 30000, "cumulative_threshold": 100000}]),
dict(name="TDS - 194C - No PAN / Invalid PAN",
category_name="Payment to Contractors (Single / Aggregate)",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 30000, "cumulative_threshold": 100000}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 30000, "cumulative_threshold": 100000}]),
dict(name="TDS - 194D - Company",
category_name="Insurance Commission",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 5, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194D - Company Assessee",
category_name="Insurance Commission",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194D - Individual",
category_name="Insurance Commission",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 5, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194D - No PAN / Invalid PAN",
category_name="Insurance Commission",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194DA - Company",
category_name="Non-exempt payments made under a life insurance policy",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
- "single_threshold": 100000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 1, "single_threshold": 100000, "cumulative_threshold": 0}]),
dict(name="TDS - 194DA - Individual",
category_name="Non-exempt payments made under a life insurance policy",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
- "single_threshold": 100000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 1, "single_threshold": 100000, "cumulative_threshold": 0}]),
dict(name="TDS - 194DA - No PAN / Invalid PAN",
category_name="Non-exempt payments made under a life insurance policy",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 100000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 100000, "cumulative_threshold": 0}]),
dict(name="TDS - 194H - Company",
category_name="Commission / Brokerage",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 5, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194H - Individual",
category_name="Commission / Brokerage",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 5, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194H - No PAN / Invalid PAN",
category_name="Commission / Brokerage",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 15000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 15000, "cumulative_threshold": 0}]),
dict(name="TDS - 194I - Rent - Company",
category_name="Rent",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 180000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 180000, "cumulative_threshold": 0}]),
dict(name="TDS - 194I - Rent - Individual",
category_name="Rent",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 180000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 180000, "cumulative_threshold": 0}]),
dict(name="TDS - 194I - Rent - No PAN / Invalid PAN",
category_name="Rent",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 180000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 180000, "cumulative_threshold": 0}]),
dict(name="TDS - 194I - Rent/Machinery - Company",
category_name="Rent-Plant / Machinery",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
- "single_threshold": 180000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 2, "single_threshold": 180000, "cumulative_threshold": 0}]),
dict(name="TDS - 194I - Rent/Machinery - Individual",
category_name="Rent-Plant / Machinery",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
- "single_threshold": 180000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 2, "single_threshold": 180000, "cumulative_threshold": 0}]),
dict(name="TDS - 194I - Rent/Machinery - No PAN / Invalid PAN",
category_name="Rent-Plant / Machinery",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 180000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 180000, "cumulative_threshold": 0}]),
dict(name="TDS - 194J - Professional Fees - Company",
category_name="Professional Fees",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 30000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 30000, "cumulative_threshold": 0}]),
dict(name="TDS - 194J - Professional Fees - Individual",
category_name="Professional Fees",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 30000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 30000, "cumulative_threshold": 0}]),
dict(name="TDS - 194J - Professional Fees - No PAN / Invalid PAN",
category_name="Professional Fees",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 30000, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 30000, "cumulative_threshold": 0}]),
dict(name="TDS - 194J - Director Fees - Company",
category_name="Director Fees",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 0, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 0, "cumulative_threshold": 0}]),
dict(name="TDS - 194J - Director Fees - Individual",
category_name="Director Fees",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 0, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 0, "cumulative_threshold": 0}]),
dict(name="TDS - 194J - Director Fees - No PAN / Invalid PAN",
category_name="Director Fees",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 0, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 0, "cumulative_threshold": 0}]),
dict(name="TDS - 194 - Dividends - Company",
category_name="Dividends",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 2500, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 2500, "cumulative_threshold": 0}]),
dict(name="TDS - 194 - Dividends - Individual",
category_name="Dividends",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
- "single_threshold": 2500, "cumulative_threshold": 0}]),
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 10, "single_threshold": 2500, "cumulative_threshold": 0}]),
dict(name="TDS - 194 - Dividends - No PAN / Invalid PAN",
category_name="Dividends",
doctype="Tax Withholding Category", accounts=accounts,
- rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
- "single_threshold": 2500, "cumulative_threshold": 0}])
+ rates=[{"from_date": fiscal_year_details[1], "to_date": fiscal_year_details[2],
+ "tax_withholding_rate": 20, "single_threshold": 2500, "cumulative_threshold": 0}])
]
def create_gratuity_rule():
diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js
index d3b7ea3..5f6dcde 100644
--- a/erpnext/regional/india/taxes.js
+++ b/erpnext/regional/india/taxes.js
@@ -49,4 +49,3 @@
}
});
}
-
diff --git a/erpnext/regional/india/test_utils.py b/erpnext/regional/india/test_utils.py
index a16f56c..2c77c8d 100644
--- a/erpnext/regional/india/test_utils.py
+++ b/erpnext/regional/india/test_utils.py
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
import unittest
-import frappe
from unittest.mock import patch
+
+import frappe
+
from erpnext.regional.india.utils import validate_document_name
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index a152797..9493614 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -1,19 +1,19 @@
from __future__ import unicode_literals
-import frappe, re, json
+
+import json
+import re
+
+import frappe
from frappe import _
-import erpnext
-from frappe.utils import cstr, flt, cint, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate
-from erpnext.regional.india import states, state_numbers
-from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
+from frappe.model.utils import get_fetch_values
+from frappe.utils import cint, cstr, date_diff, flt, getdate, nowdate
+from six import string_types
+
from erpnext.controllers.accounts_controller import get_taxes_and_charges
+from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
from erpnext.hr.utils import get_salary_assignment
from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
-from erpnext.regional.india import number_state_mapping
-from six import string_types
-from erpnext.accounts.general_ledger import make_gl_entries
-from erpnext.accounts.utils import get_account_currency
-from frappe.model.utils import get_fetch_values
-
+from erpnext.regional.india import number_state_mapping, state_numbers, states
GST_INVOICE_NUMBER_FORMAT = re.compile(r"^[a-zA-Z0-9\-/]+$") #alphanumeric and - /
GSTIN_FORMAT = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$")
@@ -112,12 +112,9 @@
frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label))
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
- if frappe.get_meta(item_doctype).has_field('gst_hsn_code'):
- return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
- else:
- return [_("Item"), _("Taxable Amount")] + tax_accounts
+ return [_("Item"), _("Taxable Amount")] + tax_accounts
-def get_itemised_tax_breakup_data(doc, account_wise=False):
+def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
itemised_tax = get_itemised_tax(doc.taxes, with_tax_account=account_wise)
itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
@@ -125,28 +122,29 @@
if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
return itemised_tax, itemised_taxable_amount
- item_hsn_map = frappe._dict()
- for d in doc.items:
- item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
+ if hsn_wise:
+ item_hsn_map = frappe._dict()
+ for d in doc.items:
+ item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
hsn_tax = {}
for item, taxes in itemised_tax.items():
- hsn_code = item_hsn_map.get(item)
- hsn_tax.setdefault(hsn_code, frappe._dict())
+ item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
+ hsn_tax.setdefault(item_or_hsn, frappe._dict())
for tax_desc, tax_detail in taxes.items():
key = tax_desc
if account_wise:
key = tax_detail.get('tax_account')
- hsn_tax[hsn_code].setdefault(key, {"tax_rate": 0, "tax_amount": 0})
- hsn_tax[hsn_code][key]["tax_rate"] = tax_detail.get("tax_rate")
- hsn_tax[hsn_code][key]["tax_amount"] += tax_detail.get("tax_amount")
+ hsn_tax[item_or_hsn].setdefault(key, {"tax_rate": 0, "tax_amount": 0})
+ hsn_tax[item_or_hsn][key]["tax_rate"] = tax_detail.get("tax_rate")
+ hsn_tax[item_or_hsn][key]["tax_amount"] += tax_detail.get("tax_amount")
# set taxable amount
hsn_taxable_amount = frappe._dict()
for item in itemised_taxable_amount:
- hsn_code = item_hsn_map.get(item)
- hsn_taxable_amount.setdefault(hsn_code, 0)
- hsn_taxable_amount[hsn_code] += itemised_taxable_amount.get(item)
+ item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
+ hsn_taxable_amount.setdefault(item_or_hsn, 0)
+ hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)
return hsn_tax, hsn_taxable_amount
@@ -251,6 +249,9 @@
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
destination_gstin = party_details.supplier_gstin
+ if not destination_gstin or party_details.gstin:
+ return False
+
if party_details.gstin == destination_gstin:
return True
else:
@@ -440,7 +441,7 @@
data.itemList = []
data.totalValue = doc.total
- data = get_item_list(data, doc)
+ data = get_item_list(data, doc, hsn_wise=True)
disable_rounded = frappe.db.get_single_value('Global Defaults', 'disable_rounded_total')
data.totInvValue = doc.grand_total if disable_rounded else doc.rounded_total
@@ -475,7 +476,7 @@
ewaybills.append(data)
data = {
- 'version': '1.0.1118',
+ 'version': '1.0.0421',
'billLists': ewaybills
}
@@ -551,7 +552,7 @@
return data
-def get_item_list(data, doc):
+def get_item_list(data, doc, hsn_wise=False):
for attr in ['cgstValue', 'sgstValue', 'igstValue', 'cessValue', 'OthValue']:
data[attr] = 0
@@ -563,7 +564,7 @@
'cess_account': ['cessRate', 'cessValue']
}
item_data_attrs = ['sgstRate', 'cgstRate', 'igstRate', 'cessRate', 'cessNonAdvol']
- hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True)
+ hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True, hsn_wise=hsn_wise)
for hsn_code, taxable_amount in hsn_taxable_amount.items():
item_data = frappe._dict()
if not hsn_code:
@@ -767,6 +768,15 @@
if tax.account_head in gst_accounts.get('cess_account', []):
doc.itc_cess_amount += flt(tax.base_tax_amount_after_discount_amount)
+def update_place_of_supply(doc, method):
+ country = frappe.get_cached_value('Company', doc.company, 'country')
+ if country != 'India':
+ return
+
+ address = frappe.db.get_value("Address", doc.get('customer_address'), ["gst_state", "gst_state_number"], as_dict=1)
+ if address and address.gst_state and address.gst_state_number:
+ doc.place_of_supply = cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
+
@frappe.whitelist()
def get_regional_round_off_accounts(company, account_list):
country = frappe.get_cached_value('Company', company, 'country')
@@ -845,24 +855,25 @@
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)
-
+
else:
rate_of_depreciation = row.rate_of_depreciation
# if its the first depreciation
if depreciable_value == asset.gross_purchase_amount:
- # as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
- diff = date_diff(row.depreciation_start_date, asset.available_for_use_date)
- if diff <= 180:
- rate_of_depreciation = rate_of_depreciation / 2
- frappe.msgprint(
- _('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.'))
+ if row.finance_book and frappe.db.get_value('Finance Book', row.finance_book, 'for_income_tax'):
+ # as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
+ diff = date_diff(row.depreciation_start_date, asset.available_for_use_date)
+ if diff <= 180:
+ rate_of_depreciation = rate_of_depreciation / 2
+ frappe.msgprint(
+ _('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.'))
depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))
return depreciation_amount
def set_item_tax_from_hsn_code(item):
- if not item.taxes and item.gst_hsn_code:
+ if not item.taxes and item.gst_hsn_code:
hsn_doc = frappe.get_doc("GST HSN Code", item.gst_hsn_code)
for tax in hsn_doc.taxes:
@@ -870,4 +881,20 @@
'item_tax_template': tax.item_tax_template,
'tax_category': tax.tax_category,
'valid_from': tax.valid_from
- })
\ No newline at end of file
+ })
+
+def delete_gst_settings_for_company(doc, method):
+ if doc.country != 'India':
+ return
+
+ gst_settings = frappe.get_doc("GST Settings")
+ records_to_delete = []
+
+ for d in reversed(gst_settings.get('gst_accounts')):
+ if d.company == doc.name:
+ records_to_delete.append(d)
+
+ for d in records_to_delete:
+ gst_settings.remove(d)
+
+ gst_settings.save()
diff --git a/erpnext/regional/italy/__init__.py b/erpnext/regional/italy/__init__.py
index ef1d582..4932f66 100644
--- a/erpnext/regional/italy/__init__.py
+++ b/erpnext/regional/italy/__init__.py
@@ -76,4 +76,4 @@
'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'}
\ No newline at end of file
+ 'Parma': 'PR', 'Varese': 'VA', 'Venezia': 'VE', 'Sassari': 'SS', 'Arezzo': 'AR'}
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
index ba1aeaf..d6c7f1d 100644
--- a/erpnext/regional/italy/utils.py
+++ b/erpnext/regional/italy/utils.py
@@ -2,13 +2,14 @@
import io
import json
+
import frappe
-from frappe.utils import flt, cstr
-from erpnext.controllers.taxes_and_totals import get_itemised_tax
from frappe import _
-from frappe.core.doctype.file.file import remove_file
+from frappe.utils import cstr, flt
+from frappe.utils.file_manager import remove_file
from six import string_types
-from frappe.desk.form.load import get_attachments
+
+from erpnext.controllers.taxes_and_totals import get_itemised_tax
from erpnext.regional.italy import state_codes
diff --git a/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json b/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json
index ab56c6b..f67e245 100644
--- a/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json
+++ b/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json
@@ -16,7 +16,7 @@
"name": "Detailed Tax Invoice",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json b/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json
index 7d8e675..b23206b 100644
--- a/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json
+++ b/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json
@@ -16,7 +16,7 @@
"name": "GST Tax Invoice",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json b/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json
index b324f6e..aed2e89 100644
--- a/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json
+++ b/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json
@@ -16,7 +16,7 @@
"name": "Simplified Tax Invoice",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/regional/print_format/tax_invoice/tax_invoice.json b/erpnext/regional/print_format/tax_invoice/tax_invoice.json
index 74db067..7479891 100644
--- a/erpnext/regional/print_format/tax_invoice/tax_invoice.json
+++ b/erpnext/regional/print_format/tax_invoice/tax_invoice.json
@@ -16,7 +16,7 @@
"name": "Tax Invoice",
"owner": "Administrator",
"print_format_builder": 1,
- "print_format_type": "Server",
+ "print_format_type": "Jinja",
"show_section_headings": 0,
"standard": "Yes"
}
\ No newline at end of file
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index a5ca7ee..c46c0df 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -10,13 +10,18 @@
from __future__ import unicode_literals
import json
+
import frappe
+from frappe import _
from six import string_types
-from frappe import _
from erpnext.accounts.utils import get_fiscal_year
-from erpnext.regional.germany.utils.datev.datev_csv import zip_and_download, get_datev_csv
-from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames
+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 = [
{
@@ -44,6 +49,12 @@
"width": 100
},
{
+ "label": "BU-Schlüssel",
+ "fieldname": "BU-Schlüssel",
+ "fieldtype": "Data",
+ "width": 100
+ },
+ {
"label": "Belegdatum",
"fieldname": "Belegdatum",
"fieldtype": "Date",
@@ -114,6 +125,36 @@
"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
}
]
@@ -161,6 +202,125 @@
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.
@@ -171,8 +331,7 @@
filters -- dict of filters to be passed to the sql query
as_dict -- return as list of dicts [0,1]
"""
- filter_by_voucher = 'AND gl.voucher_type = %(voucher_type)s' if filters.get('voucher_type') else ''
- gl_entries = frappe.db.sql("""
+ query = """
SELECT
/* either debit or credit amount; always positive */
@@ -187,6 +346,9 @@
/* 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',
LEFT(gl.remarks, 60) as 'Buchungstext',
@@ -199,30 +361,34 @@
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 `tabAccount` acc
+ ON gl.account = acc.name
- left join `tabCustomer` cus
- on gl.party_type = 'Customer'
- and gl.party = cus.name
+ LEFT JOIN `tabParty Account` par
+ ON par.parent = gl.party
+ AND par.parenttype = gl.party_type
+ AND par.company = %(company)s
- left join `tabSupplier` sup
- on gl.party_type = 'Supplier'
- and gl.party = sup.name
+ {extra_joins}
- left join `tabParty Account` par
- on par.parent = gl.party
- and par.parenttype = gl.party_type
- and par.company = %(company)s
-
- WHERE gl.company = %(company)s
+ WHERE gl.company = %(company)s
AND DATE(gl.posting_date) >= %(from_date)s
AND DATE(gl.posting_date) <= %(to_date)s
- {}
- ORDER BY 'Belegdatum', gl.voucher_no""".format(filter_by_voucher), filters, as_dict=as_dict)
+
+ {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
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
index 59b878e..b538893 100644
--- a/erpnext/regional/report/datev/test_datev.py
+++ b/erpnext/regional/report/datev/test_datev.py
@@ -2,21 +2,27 @@
from __future__ import unicode_literals
import zipfile
-import frappe
-from six import BytesIO
from unittest import TestCase
-from frappe.utils import today, now_datetime, cstr
+
+import frappe
+from frappe.utils import cstr, now_datetime, today
+from six import BytesIO
+
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-
-from erpnext.regional.report.datev.datev import validate
-from erpnext.regional.report.datev.datev import get_transactions
-from erpnext.regional.report.datev.datev import get_customers
-from erpnext.regional.report.datev.datev import get_suppliers
-from erpnext.regional.report.datev.datev import get_account_names
-from erpnext.regional.report.datev.datev import download_datev_csv
-
+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.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames
+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):
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js
deleted file mode 100644
index 4713217..0000000
--- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-/* eslint-disable */
-
-frappe.query_reports["E-Invoice Summary"] = {
- "filters": [
- {
- "fieldtype": "Link",
- "options": "Company",
- "reqd": 1,
- "fieldname": "company",
- "label": __("Company"),
- "default": frappe.defaults.get_user_default("Company"),
- },
- {
- "fieldtype": "Link",
- "options": "Customer",
- "fieldname": "customer",
- "label": __("Customer")
- },
- {
- "fieldtype": "Date",
- "reqd": 1,
- "fieldname": "from_date",
- "label": __("From Date"),
- "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
- },
- {
- "fieldtype": "Date",
- "reqd": 1,
- "fieldname": "to_date",
- "label": __("To Date"),
- "default": frappe.datetime.get_today(),
- },
- {
- "fieldtype": "Select",
- "fieldname": "status",
- "label": __("Status"),
- "options": "\nPending\nGenerated\nCancelled\nFailed"
- }
- ],
-
- "formatter": function (value, row, column, data, default_formatter) {
- value = default_formatter(value, row, column, data);
-
- if (column.fieldname == "einvoice_status" && value) {
- if (value == 'Pending') value = `<span class="bold" style="color: var(--text-on-orange)">${value}</span>`;
- else if (value == 'Generated') value = `<span class="bold" style="color: var(--text-on-green)">${value}</span>`;
- else if (value == 'Cancelled') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`;
- else if (value == 'Failed') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`;
- }
-
- return value;
- }
-};
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
deleted file mode 100644
index d0000ad..0000000
--- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "add_total_row": 0,
- "columns": [],
- "creation": "2021-03-12 11:23:37.312294",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "filters": [],
- "idx": 0,
- "is_standard": "Yes",
- "json": "{}",
- "letter_head": "Logo",
- "modified": "2021-03-13 12:36:48.689413",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "E-Invoice Summary",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Sales Invoice",
- "report_name": "E-Invoice Summary",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Administrator"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py
deleted file mode 100644
index 47acf29..0000000
--- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def execute(filters=None):
- validate_filters(filters)
-
- columns = get_columns()
- data = get_data(filters)
-
- return columns, data
-
-def validate_filters(filters={}):
- filters = frappe._dict(filters)
-
- if not filters.company:
- frappe.throw(_('{} is mandatory for generating E-Invoice Summary Report').format(_('Company')), title=_('Invalid Filter'))
- if filters.company:
- # validate if company has e-invoicing enabled
- pass
- if not filters.from_date or not filters.to_date:
- frappe.throw(_('From Date & To Date is mandatory for generating E-Invoice Summary Report'), title=_('Invalid Filter'))
- if filters.from_date > filters.to_date:
- frappe.throw(_('From Date must be before To Date'), title=_('Invalid Filter'))
-
-def get_data(filters={}):
- query_filters = {
- 'posting_date': ['between', [filters.from_date, filters.to_date]],
- 'einvoice_status': ['is', 'set'],
- 'company': filters.company
- }
- if filters.customer:
- query_filters['customer'] = filters.customer
- if filters.status:
- query_filters['einvoice_status'] = filters.status
-
- data = frappe.get_all(
- 'Sales Invoice',
- filters=query_filters,
- fields=[d.get('fieldname') for d in get_columns()]
- )
-
- return data
-
-def get_columns():
- return [
- {
- "fieldtype": "Date",
- "fieldname": "posting_date",
- "label": _("Posting Date"),
- "width": 0
- },
- {
- "fieldtype": "Link",
- "fieldname": "name",
- "label": _("Sales Invoice"),
- "options": "Sales Invoice",
- "width": 140
- },
- {
- "fieldtype": "Data",
- "fieldname": "einvoice_status",
- "label": _("Status"),
- "width": 100
- },
- {
- "fieldtype": "Link",
- "fieldname": "customer",
- "options": "Customer",
- "label": _("Customer")
- },
- {
- "fieldtype": "Check",
- "fieldname": "is_return",
- "label": _("Is Return"),
- "width": 85
- },
- {
- "fieldtype": "Data",
- "fieldname": "ack_no",
- "label": "Ack. No.",
- "width": 145
- },
- {
- "fieldtype": "Data",
- "fieldname": "ack_date",
- "label": "Ack. Date",
- "width": 165
- },
- {
- "fieldtype": "Data",
- "fieldname": "irn",
- "label": _("IRN No."),
- "width": 250
- },
- {
- "fieldtype": "Currency",
- "options": "Company:company:default_currency",
- "fieldname": "base_grand_total",
- "label": _("Grand Total"),
- "width": 120
- }
- ]
\ No newline at end of file
diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
index 67297f7..d7e3ac9 100644
--- a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
@@ -41,7 +41,7 @@
var w = window.open(
frappe.urllib.get_full_url(
- "/api/method/erpnext.regional.italy.utils.export_invoices?"
+ "/api/method/erpnext.regional.italy.utils.export_invoices?"
+ "filters=" + JSON.stringify(reportview.get_filter_values())
)
);
diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
index 376ba3e..f4ce7a7 100644
--- a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
@@ -2,7 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from erpnext.accounts.report.sales_register.sales_register import _execute
+
def execute(filters=None):
return _execute(filters)
diff --git a/erpnext/regional/report/eway_bill/eway_bill.py b/erpnext/regional/report/eway_bill/eway_bill.py
index 5b9896b..c78084f 100644
--- a/erpnext/regional/report/eway_bill/eway_bill.py
+++ b/erpnext/regional/report/eway_bill/eway_bill.py
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
import re
+
+import frappe
from frappe import _
from frappe.utils import nowdate
+
def execute(filters=None):
if not filters: filters.setdefault('posting_date', [nowdate(), nowdate()])
columns, data = [], []
@@ -41,7 +44,7 @@
}
# Regular expression set to remove all the special characters
- special_characters = "[$%^*()+\\[\]{};':\"\\|<>.?]"
+ special_characters = r"[$%^*()+\\[\]{};':\"\\|<>.?]"
for row in data:
set_defaults(row)
@@ -388,4 +391,4 @@
},
]
- return columns
\ No newline at end of file
+ return columns
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 e903c9f..9567916 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"
@@ -2,11 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import format_datetime
-from frappe import _
+
import re
+import frappe
+from frappe import _
+from frappe.utils import format_datetime
+
+
def execute(filters=None):
account_details = {}
for acc in frappe.db.sql("""select name, is_group from tabAccount""", as_dict=1):
@@ -116,7 +119,7 @@
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("{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")
diff --git a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py
index b5948f9..092f72a 100644
--- a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py
+++ b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py
@@ -3,7 +3,10 @@
from __future__ import unicode_literals
-from erpnext.accounts.report.item_wise_purchase_register.item_wise_purchase_register import _execute
+from erpnext.accounts.report.item_wise_purchase_register.item_wise_purchase_register import (
+ _execute,
+)
+
def execute(filters=None):
return _execute(filters, additional_table_columns=[
diff --git a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py
index e13f509..44f623b 100644
--- a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py
+++ b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py
@@ -5,6 +5,7 @@
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import _execute
+
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', fieldname="customer_gstin", width=120),
diff --git a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js
index 2b4359a..bbcd355 100644
--- a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js
+++ b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js
@@ -4,4 +4,4 @@
{% include "erpnext/accounts/report/purchase_register/purchase_register.js" %}
-frappe.query_reports["GST Purchase Register"] = frappe.query_reports["Purchase Register"]
\ No newline at end of file
+frappe.query_reports["GST Purchase Register"] = frappe.query_reports["Purchase Register"]
diff --git a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py
index 7274e0a..e972444 100644
--- a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py
+++ b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py
@@ -5,6 +5,7 @@
from erpnext.accounts.report.purchase_register.purchase_register import _execute
+
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Supplier GSTIN', fieldname="supplier_gstin", width=120),
@@ -21,4 +22,3 @@
'export_type',
'ecommerce_gstin'
])
-
diff --git a/erpnext/regional/report/gst_sales_register/gst_sales_register.py b/erpnext/regional/report/gst_sales_register/gst_sales_register.py
index 075bd48..6975af3 100644
--- a/erpnext/regional/report/gst_sales_register/gst_sales_register.py
+++ b/erpnext/regional/report/gst_sales_register/gst_sales_register.py
@@ -5,6 +5,7 @@
from erpnext.accounts.report.sales_register.sales_register import _execute
+
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', fieldname="customer_gstin", width=120),
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index 444f5db..ef2bdb6 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -51,7 +51,9 @@
{ "value": "B2C Large", "label": __("B2C(Large) Invoices - 5A, 5B") },
{ "value": "B2C Small", "label": __("B2C(Small) Invoices - 7") },
{ "value": "CDNR-REG", "label": __("Credit/Debit Notes (Registered) - 9B") },
- { "value": "EXPORT", "label": __("Export Invoice - 6A") }
+ { "value": "CDNR-UNREG", "label": __("Credit/Debit Notes (Unregistered) - 9B") },
+ { "value": "EXPORT", "label": __("Export Invoice - 6A") },
+ { "value": "Advances", "label": __("Tax Liability (Advances Received) - 11A(1), 11A(2)") }
],
"default": "B2B"
}
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 4b73094..23924c5 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -2,14 +2,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe import _
-from frappe.utils import flt, formatdate, now_datetime, getdate
+
+import json
from datetime import date
+
+import frappe
+from frappe import _
+from frappe.utils import flt, formatdate, getdate
from six import iteritems
-from erpnext.regional.doctype.gstr_3b_report.gstr_3b_report import get_period
+
from erpnext.regional.india.utils import get_gst_accounts
+
def execute(filters=None):
return Gstr1Report(filters).run()
@@ -50,63 +54,83 @@
self.get_invoice_items()
self.get_items_based_on_tax_rate()
self.invoice_fields = [d["fieldname"] for d in self.invoice_columns]
- self.get_data()
+
+ self.get_data()
return self.columns, self.data
def get_data(self):
if self.filters.get("type_of_business") in ("B2C Small", "B2C Large"):
self.get_b2c_data()
- else:
+ elif self.filters.get("type_of_business") == "Advances":
+ self.get_advance_data()
+ elif self.invoices:
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv)
for rate, items in items_based_on_rate.items():
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
- if self.filters.get("type_of_business") == "CDNR-REG":
+ if self.filters.get("type_of_business") in ("CDNR-REG", "CDNR-UNREG"):
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
row.append("C" if invoice_details.is_return else "D")
if taxable_value:
self.data.append(row)
+ def get_advance_data(self):
+ advances_data = {}
+ advances = self.get_advance_entries()
+ for entry in advances:
+ # only consider IGST and SGST so as to avoid duplication of taxable amount
+ if entry.account_head in self.gst_accounts.igst_account or \
+ entry.account_head in self.gst_accounts.sgst_account:
+ advances_data.setdefault((entry.place_of_supply, entry.rate), [0.0, 0.0])
+ advances_data[(entry.place_of_supply, entry.rate)][0] += (entry.amount * 100 / entry.rate)
+ elif entry.account_head in self.gst_accounts.cess_account:
+ advances_data[(entry.place_of_supply, entry.rate)][1] += entry.amount
+
+ for key, value in advances_data.items():
+ row= [key[0], key[1], value[0], value[1]]
+ self.data.append(row)
+
def get_b2c_data(self):
b2cs_output = {}
- for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
- invoice_details = self.invoices.get(inv)
- for rate, items in items_based_on_rate.items():
- place_of_supply = invoice_details.get("place_of_supply")
- ecommerce_gstin = invoice_details.get("ecommerce_gstin")
+ if self.invoices:
+ for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
+ invoice_details = self.invoices.get(inv)
+ for rate, items in items_based_on_rate.items():
+ place_of_supply = invoice_details.get("place_of_supply")
+ ecommerce_gstin = invoice_details.get("ecommerce_gstin")
- b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin),{
- "place_of_supply": "",
- "ecommerce_gstin": "",
- "rate": "",
- "taxable_value": 0,
- "cess_amount": 0,
- "type": "",
- "invoice_number": invoice_details.get("invoice_number"),
- "posting_date": invoice_details.get("posting_date"),
- "invoice_value": invoice_details.get("base_grand_total"),
- })
+ b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin), {
+ "place_of_supply": "",
+ "ecommerce_gstin": "",
+ "rate": "",
+ "taxable_value": 0,
+ "cess_amount": 0,
+ "type": "",
+ "invoice_number": invoice_details.get("invoice_number"),
+ "posting_date": invoice_details.get("posting_date"),
+ "invoice_value": invoice_details.get("base_grand_total"),
+ })
- row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
- row["place_of_supply"] = place_of_supply
- row["ecommerce_gstin"] = ecommerce_gstin
- row["rate"] = rate
- row["taxable_value"] += sum([abs(net_amount)
- for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items])
- row["cess_amount"] += flt(self.invoice_cess.get(inv), 2)
- row["type"] = "E" if ecommerce_gstin else "OE"
+ row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
+ row["place_of_supply"] = place_of_supply
+ row["ecommerce_gstin"] = ecommerce_gstin
+ row["rate"] = rate
+ row["taxable_value"] += sum([abs(net_amount)
+ for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items])
+ row["cess_amount"] += flt(self.invoice_cess.get(inv), 2)
+ row["type"] = "E" if ecommerce_gstin else "OE"
- for key, value in iteritems(b2cs_output):
- self.data.append(value)
+ for key, value in iteritems(b2cs_output):
+ self.data.append(value)
def get_row_data_for_invoice(self, invoice, invoice_details, tax_rate, items):
row = []
for fieldname in self.invoice_fields:
- if self.filters.get("type_of_business") == "CDNR-REG" and fieldname == "invoice_value":
+ if self.filters.get("type_of_business") in ("CDNR-REG", "CDNR-UNREG") and fieldname == "invoice_value":
row.append(abs(invoice_details.base_rounded_total) or abs(invoice_details.base_grand_total))
elif fieldname == "invoice_value":
row.append(invoice_details.base_rounded_total or invoice_details.base_grand_total)
@@ -150,9 +174,10 @@
company_gstins = get_company_gstin_number(self.filters.get('company'), all_gstins=True)
- self.filters.update({
- 'company_gstins': company_gstins
- })
+ if company_gstins:
+ self.filters.update({
+ 'company_gstins': company_gstins
+ })
invoice_data = frappe.db.sql("""
select
@@ -167,6 +192,16 @@
for d in invoice_data:
self.invoices.setdefault(d.invoice_number, d)
+ def get_advance_entries(self):
+ return frappe.db.sql("""
+ SELECT SUM(a.base_tax_amount) as amount, a.account_head, a.rate, p.place_of_supply
+ FROM `tabPayment Entry` p, `tabAdvance Taxes and Charges` a
+ WHERE p.docstatus = 1
+ AND p.name = a.parent
+ AND posting_date between %s and %s
+ GROUP BY a.account_head, p.place_of_supply, a.rate
+ """, (self.filters.get('from_date'), self.filters.get('to_date')), as_dict=1)
+
def get_conditions(self):
conditions = ""
@@ -179,7 +214,7 @@
if self.filters.get("type_of_business") == "B2B":
- conditions += "AND IFNULL(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ') AND is_return != 1"
+ conditions += "AND IFNULL(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ') AND is_return != 1 AND is_debit_note !=1"
if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"):
b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit')
@@ -188,7 +223,7 @@
if self.filters.get("type_of_business") == "B2C Large":
conditions += """ AND ifnull(SUBSTR(place_of_supply, 1, 2),'') != ifnull(SUBSTR(company_gstin, 1, 2),'')
- AND grand_total > {0} AND is_return != 1 and gst_category ='Unregistered' """.format(flt(b2c_limit))
+ AND grand_total > {0} AND is_return != 1 AND is_debit_note !=1 AND gst_category ='Unregistered' """.format(flt(b2c_limit))
elif self.filters.get("type_of_business") == "B2C Small":
conditions += """ AND (
@@ -198,6 +233,12 @@
elif self.filters.get("type_of_business") == "CDNR-REG":
conditions += """ AND (is_return = 1 OR is_debit_note = 1) AND IFNULL(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ')"""
+ elif self.filters.get("type_of_business") == "CDNR-UNREG":
+ b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit')
+ conditions += """ AND ifnull(SUBSTR(place_of_supply, 1, 2),'') != ifnull(SUBSTR(company_gstin, 1, 2),'')
+ AND (is_return = 1 OR is_debit_note = 1)
+ AND IFNULL(gst_category, '') in ('Unregistered', 'Overseas')"""
+
elif self.filters.get("type_of_business") == "EXPORT":
conditions += """ AND is_return !=1 and gst_category = 'Overseas' """
@@ -503,6 +544,84 @@
"width": 80
}
]
+ elif self.filters.get("type_of_business") == "CDNR-UNREG":
+ self.invoice_columns = [
+ {
+ "fieldname": "customer_name",
+ "label": "Receiver Name",
+ "fieldtype": "Data",
+ "width": 120
+ },
+ {
+ "fieldname": "return_against",
+ "label": "Issued Against",
+ "fieldtype": "Link",
+ "options": "Sales Invoice",
+ "width": 120
+ },
+ {
+ "fieldname": "posting_date",
+ "label": "Note Date",
+ "fieldtype": "Date",
+ "width": 120
+ },
+ {
+ "fieldname": "invoice_number",
+ "label": "Note Number",
+ "fieldtype": "Link",
+ "options": "Sales Invoice",
+ "width":120
+ },
+ {
+ "fieldname": "export_type",
+ "label": "Export Type",
+ "fieldtype": "Data",
+ "hidden": 1
+ },
+ {
+ "fieldname": "reason_for_issuing_document",
+ "label": "Reason For Issuing document",
+ "fieldtype": "Data",
+ "width": 140
+ },
+ {
+ "fieldname": "place_of_supply",
+ "label": "Place Of Supply",
+ "fieldtype": "Data",
+ "width": 120
+ },
+ {
+ "fieldname": "gst_category",
+ "label": "GST Category",
+ "fieldtype": "Data"
+ },
+ {
+ "fieldname": "invoice_value",
+ "label": "Invoice Value",
+ "fieldtype": "Currency",
+ "width": 120
+ }
+ ]
+ self.other_columns = [
+ {
+ "fieldname": "cess_amount",
+ "label": "Cess Amount",
+ "fieldtype": "Currency",
+ "width": 100
+ },
+ {
+ "fieldname": "pre_gst",
+ "label": "PRE GST",
+ "fieldtype": "Data",
+ "width": 80
+ },
+ {
+ "fieldname": "document_type",
+ "label": "Document Type",
+ "fieldtype": "Data",
+ "width": 80
+ }
+ ]
elif self.filters.get("type_of_business") == "B2C Small":
self.invoice_columns = [
{
@@ -578,6 +697,25 @@
"width": 120
}
]
+ elif self.filters.get("type_of_business") == "Advances":
+ self.invoice_columns = [
+ {
+ "fieldname": "place_of_supply",
+ "label": "Place Of Supply",
+ "fieldtype": "Data",
+ "width": 120
+ }
+ ]
+
+ self.other_columns = [
+ {
+ "fieldname": "cess_amount",
+ "label": "Cess Amount",
+ "fieldtype": "Currency",
+ "width": 100
+ }
+ ]
+
self.columns = self.invoice_columns + self.tax_columns + self.other_columns
@frappe.whitelist()
@@ -588,7 +726,7 @@
fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
- gst_json = {"version": "GST2.2.9",
+ gst_json = {"version": "GST3.0.4",
"hash": "hash", "gstin": gstin, "fp": fp}
res = {}
@@ -616,12 +754,29 @@
out = get_export_json(res)
gst_json["exp"] = out
- elif filters["type_of_business"] == 'CDNR-REG':
+ elif filters["type_of_business"] == "CDNR-REG":
for item in report_data[:-1]:
res.setdefault(item["customer_gstin"], {}).setdefault(item["invoice_number"],[]).append(item)
out = get_cdnr_reg_json(res, gstin)
gst_json["cdnr"] = out
+ elif filters["type_of_business"] == "CDNR-UNREG":
+ for item in report_data[:-1]:
+ res.setdefault(item["invoice_number"],[]).append(item)
+
+ out = get_cdnr_unreg_json(res, gstin)
+ gst_json["cdnur"] = out
+
+ elif filters["type_of_business"] == "Advances":
+ for item in report_data[:-1]:
+ if not item.get("place_of_supply"):
+ frappe.throw(_("""{0} not entered in some entries.
+ Please update and try again""").format(frappe.bold("Place Of Supply")))
+
+ res.setdefault(item["place_of_supply"],[]).append(item)
+
+ out = get_advances_json(res, gstin)
+ gst_json["at"] = out
return {
'report_name': report_name,
@@ -701,6 +856,40 @@
return out
+def get_advances_json(data, gstin):
+ company_state_number = gstin[0:2]
+ out = []
+ for place_of_supply, items in iteritems(data):
+ supply_type = "INTRA" if company_state_number == place_of_supply.split('-')[0] else "INTER"
+ row = {
+ "pos": place_of_supply.split('-')[0],
+ "itms": [],
+ "sply_ty": supply_type
+ }
+
+ for item in items:
+ itms = {
+ 'rt': item['rate'],
+ 'ad_amount': flt(item.get('taxable_value')),
+ 'csamt': flt(item.get('cess_amount'))
+ }
+
+ if supply_type == "INTRA":
+ itms.update({
+ "samt": flt((itms["ad_amount"] * itms["rt"]) / 100),
+ "camt": flt((itms["ad_amount"] * itms["rt"]) / 100),
+ "rt": itms["rt"] * 2
+ })
+ else:
+ itms.update({
+ "iamt": flt((itms["ad_amount"] * itms["rt"]) / 100)
+ })
+
+ row['itms'].append(itms)
+ out.append(row)
+
+ return out
+
def get_b2cl_json(res, gstin):
out = []
for pos in res:
@@ -765,7 +954,7 @@
"ntty": invoice[0]["document_type"],
"pos": "%02d" % int(invoice[0]["place_of_supply"].split('-')[0]),
"rchrg": invoice[0]["reverse_charge"],
- "inv_type": get_invoice_type_for_cdnr(invoice[0])
+ "inv_typ": get_invoice_type_for_cdnr(invoice[0])
}
inv_item["itms"] = []
@@ -780,6 +969,27 @@
return out
+def get_cdnr_unreg_json(res, gstin):
+ out = []
+
+ for invoice, items in iteritems(res):
+ inv_item = {
+ "nt_num": items[0]["invoice_number"],
+ "nt_dt": getdate(items[0]["posting_date"]).strftime('%d-%m-%Y'),
+ "val": abs(flt(items[0]["invoice_value"])),
+ "ntty": items[0]["document_type"],
+ "pos": "%02d" % int(items[0]["place_of_supply"].split('-')[0]),
+ "typ": get_invoice_type_for_cdnrur(items[0])
+ }
+
+ inv_item["itms"] = []
+ for item in items:
+ inv_item["itms"].append(get_rate_and_tax_details(item, gstin))
+
+ out.append(inv_item)
+
+ return out
+
def get_invoice_type_for_cdnr(row):
if row.get('gst_category') == 'SEZ':
if row.get('export_type') == 'WPAY':
@@ -787,12 +997,23 @@
else:
invoice_type = 'SEWOP'
elif row.get('gst_category') == 'Deemed Export':
- row.invoice_type = 'DE'
+ invoice_type = 'DE'
elif row.get('gst_category') == 'Registered Regular':
invoice_type = 'R'
return invoice_type
+def get_invoice_type_for_cdnrur(row):
+ if row.get('gst_category') == 'Overseas':
+ if row.get('export_type') == 'WPAY':
+ invoice_type = 'EXPWP'
+ else:
+ invoice_type = 'EXPWOP'
+ elif row.get('gst_category') == 'Unregistered':
+ invoice_type = 'B2CL'
+
+ return invoice_type
+
def get_basic_invoice_detail(row):
return {
"inum": row["invoice_number"],
@@ -831,8 +1052,9 @@
["Dynamic Link", "link_doctype", "=", "Company"],
["Dynamic Link", "link_name", "=", company],
["Dynamic Link", "parenttype", "=", "Address"],
+ ["gstin", "!=", '']
]
- gstin = frappe.get_all("Address", filters=filters, pluck="gstin")
+ gstin = frappe.get_all("Address", filters=filters, pluck="gstin", order_by="is_primary_address desc")
if gstin and not all_gstins:
gstin = gstin[0]
diff --git a/erpnext/regional/report/gstr_2/gstr_2.py b/erpnext/regional/report/gstr_2/gstr_2.py
index 616c2b8..5e44955 100644
--- a/erpnext/regional/report/gstr_2/gstr_2.py
+++ b/erpnext/regional/report/gstr_2/gstr_2.py
@@ -2,10 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from datetime import date
+
+import frappe
+
from erpnext.regional.report.gstr_1.gstr_1 import Gstr1Report
+
def execute(filters=None):
return Gstr2Report(filters).run()
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
index 59389ce..7a938c7 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
@@ -2,16 +2,20 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe import _
-from frappe.utils import flt, getdate, cstr
-from frappe.model.meta import get_field_precision
-from frappe.utils.xlsxutils import handle_html
-from six import iteritems
+
import json
+
+import frappe
+from frappe import _
+from frappe.model.meta import get_field_precision
+from frappe.utils import cstr, flt, getdate
+from six import iteritems
+
+import erpnext
from erpnext.regional.india.utils import get_gst_accounts
from erpnext.regional.report.gstr_1.gstr_1 import get_company_gstin_number
+
def execute(filters=None):
return _execute(filters)
@@ -285,5 +289,3 @@
count +=1
return data
-
-
diff --git a/erpnext/regional/report/india_gst_common/india_gst_common.js b/erpnext/regional/report/india_gst_common/india_gst_common.js
index 4960601..bddc320 100644
--- a/erpnext/regional/report/india_gst_common/india_gst_common.js
+++ b/erpnext/regional/report/india_gst_common/india_gst_common.js
@@ -18,4 +18,4 @@
company_gstins.df.options = [""];
company_gstins.refresh();
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py
index 4e57ff7..b1a5d10 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.py
+++ b/erpnext/regional/report/irs_1099/irs_1099.py
@@ -3,16 +3,16 @@
import json
-from PyPDF2 import PdfFileWriter
-
import frappe
-from erpnext.accounts.utils import get_fiscal_year
from frappe import _
from frappe.utils import cstr, nowdate
from frappe.utils.data import fmt_money
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 erpnext.accounts.utils import get_fiscal_year
IRS_1099_FORMS_FILE_EXTENSION = ".pdf"
@@ -52,7 +52,7 @@
AND gl.party_type = "Supplier"
AND gl.company = %(company)s
{conditions}
-
+
GROUP BY
gl.party
diff --git a/erpnext/regional/report/e_invoice_summary/__init__.py b/erpnext/regional/report/ksa_vat/__init__.py
similarity index 100%
rename from erpnext/regional/report/e_invoice_summary/__init__.py
rename to erpnext/regional/report/ksa_vat/__init__.py
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.js b/erpnext/regional/report/ksa_vat/ksa_vat.js
new file mode 100644
index 0000000..d46d260
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.js
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, Havenir Solutions and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["KSA VAT"] = {
+ onload() {
+ frappe.breadcrumbs.add('Accounts');
+ },
+ "filters": [
+ {
+ "fieldname": "company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "reqd": 1,
+ "default": frappe.defaults.get_user_default("Company")
+ },
+ {
+ "fieldname": "from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ },
+ {
+ "fieldname": "to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.get_today()
+ }
+ ],
+ "formatter": function(value, row, column, data, default_formatter) {
+ if (data
+ && (data.title=='VAT on Sales' || data.title=='VAT on Purchases')
+ && data.title==value) {
+ value = $(`<span>${value}</span>`);
+ var $value = $(value).css("font-weight", "bold");
+ value = $value.wrap("<p></p>").parent().html();
+ return value
+ }else if (data.title=='Grand Total'){
+ if (data.title==value) {
+ value = $(`<span>${value}</span>`);
+ var $value = $(value).css("font-weight", "bold");
+ value = $value.wrap("<p></p>").parent().html();
+ return value
+ }else{
+ value = default_formatter(value, row, column, data);
+ value = $(`<span>${value}</span>`);
+ var $value = $(value).css("font-weight", "bold");
+ value = $value.wrap("<p></p>").parent().html();
+ console.log($value)
+ return value
+ }
+ }else{
+ value = default_formatter(value, row, column, data);
+ return value;
+ }
+ },
+};
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.json b/erpnext/regional/report/ksa_vat/ksa_vat.json
new file mode 100644
index 0000000..036e260
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-07-13 08:54:38.000949",
+ "disable_prepared_report": 1,
+ "disabled": 1,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-08-26 04:14:37.202594",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT",
+ "owner": "Administrator",
+ "prepared_report": 1,
+ "ref_doctype": "GL Entry",
+ "report_name": "KSA VAT",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "System Manager"
+ },
+ {
+ "role": "Accounts Manager"
+ },
+ {
+ "role": "Accounts User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py
new file mode 100644
index 0000000..a42ebc9
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2013, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import json
+
+import frappe
+from frappe import _
+from frappe.utils import get_url_to_list
+
+
+def execute(filters=None):
+ columns = columns = get_columns()
+ data = get_data(filters)
+ return columns, data
+
+def get_columns():
+ return [
+ {
+ "fieldname": "title",
+ "label": _("Title"),
+ "fieldtype": "Data",
+ "width": 300
+ },
+ {
+ "fieldname": "amount",
+ "label": _("Amount (SAR)"),
+ "fieldtype": "Currency",
+ "width": 150,
+ },
+ {
+ "fieldname": "adjustment_amount",
+ "label": _("Adjustment (SAR)"),
+ "fieldtype": "Currency",
+ "width": 150,
+ },
+ {
+ "fieldname": "vat_amount",
+ "label": _("VAT Amount (SAR)"),
+ "fieldtype": "Currency",
+ "width": 150,
+ }
+ ]
+
+def get_data(filters):
+ data = []
+
+ # Validate if vat settings exist
+ company = filters.get('company')
+ if frappe.db.exists('KSA VAT Setting', company) is None:
+ url = get_url_to_list('KSA VAT Setting')
+ frappe.msgprint(_('Create <a href="{}">KSA VAT Setting</a> for this company').format(url))
+ return data
+
+ ksa_vat_setting = frappe.get_doc('KSA VAT Setting', company)
+
+ # Sales Heading
+ append_data(data, 'VAT on Sales', '', '', '')
+
+ grand_total_taxable_amount = 0
+ grand_total_taxable_adjustment_amount = 0
+ grand_total_tax = 0
+
+ for vat_setting in ksa_vat_setting.ksa_vat_sales_accounts:
+ total_taxable_amount, total_taxable_adjustment_amount, \
+ total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Sales Invoice')
+
+ # Adding results to data
+ append_data(data, vat_setting.title, total_taxable_amount,
+ total_taxable_adjustment_amount, total_tax)
+
+ grand_total_taxable_amount += total_taxable_amount
+ grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
+ grand_total_tax += total_tax
+
+ # Sales Grand Total
+ append_data(data, 'Grand Total', grand_total_taxable_amount,
+ grand_total_taxable_adjustment_amount, grand_total_tax)
+
+ # Blank Line
+ append_data(data, '', '', '', '')
+
+ # Purchase Heading
+ append_data(data, 'VAT on Purchases', '', '', '')
+
+ grand_total_taxable_amount = 0
+ grand_total_taxable_adjustment_amount = 0
+ grand_total_tax = 0
+
+ for vat_setting in ksa_vat_setting.ksa_vat_purchase_accounts:
+ total_taxable_amount, total_taxable_adjustment_amount, \
+ total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Purchase Invoice')
+
+ # Adding results to data
+ append_data(data, vat_setting.title, total_taxable_amount,
+ total_taxable_adjustment_amount, total_tax)
+
+ grand_total_taxable_amount += total_taxable_amount
+ grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
+ grand_total_tax += total_tax
+
+ # Purchase Grand Total
+ append_data(data, 'Grand Total', grand_total_taxable_amount,
+ grand_total_taxable_adjustment_amount, grand_total_tax)
+
+ return data
+
+def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype):
+ '''
+ (KSA, {filters}, 'Sales Invoice') => 500, 153, 10 \n
+ calculates and returns \n
+ total_taxable_amount, total_taxable_adjustment_amount, total_tax'''
+ from_date = filters.get('from_date')
+ to_date = filters.get('to_date')
+
+ # Initiate variables
+ total_taxable_amount = 0
+ total_taxable_adjustment_amount = 0
+ total_tax = 0
+ # Fetch All Invoices
+ invoices = frappe.get_list(doctype,
+ filters ={
+ 'docstatus': 1,
+ 'posting_date': ['between', [from_date, to_date]]
+ }, fields =['name', 'is_return'])
+
+ for invoice in invoices:
+ invoice_items = frappe.get_list(f'{doctype} Item',
+ filters ={
+ 'docstatus': 1,
+ 'parent': invoice.name,
+ 'item_tax_template': vat_setting.item_tax_template
+ }, fields =['item_code', 'net_amount'])
+
+ for item in invoice_items:
+ # Summing up total taxable amount
+ if invoice.is_return == 0:
+ total_taxable_amount += item.net_amount
+
+ if invoice.is_return == 1:
+ total_taxable_adjustment_amount += item.net_amount
+
+ # Summing up total tax
+ total_tax += get_tax_amount(item.item_code, vat_setting.account, doctype, invoice.name)
+
+ return total_taxable_amount, total_taxable_adjustment_amount, total_tax
+
+
+
+def append_data(data, title, amount, adjustment_amount, vat_amount):
+ """Returns data with appended value."""
+ data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount})
+
+def get_tax_amount(item_code, account_head, doctype, parent):
+ if doctype == 'Sales Invoice':
+ tax_doctype = 'Sales Taxes and Charges'
+
+ elif doctype == 'Purchase Invoice':
+ tax_doctype = 'Purchase Taxes and Charges'
+
+ item_wise_tax_detail = frappe.get_value(tax_doctype, {
+ 'docstatus': 1,
+ 'parent': parent,
+ 'account_head': account_head
+ }, 'item_wise_tax_detail')
+
+ tax_amount = 0
+ if item_wise_tax_detail and len(item_wise_tax_detail) > 0:
+ item_wise_tax_detail = json.loads(item_wise_tax_detail)
+ for key, value in item_wise_tax_detail.items():
+ if key == item_code:
+ tax_amount = value[1]
+ break
+
+ return tax_amount
diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js
index 29c7dbf..bb75238 100644
--- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js
@@ -4,4 +4,4 @@
frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
frappe.query_reports["Professional Tax Deductions"] = erpnext.salary_slip_deductions_report_filters;
-});
\ No newline at end of file
+});
diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
index acde68a..5300b92 100644
--- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
@@ -2,9 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions
+
+from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import (
+ get_conditions,
+)
+
def execute(filters=None):
data = get_data(filters)
@@ -69,4 +74,4 @@
data.append(employee)
- return data
\ No newline at end of file
+ return data
diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js
index b4dc28d..a91a307 100644
--- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js
@@ -4,4 +4,4 @@
frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
frappe.query_reports["Provident Fund Deductions"] = erpnext.salary_slip_deductions_report_filters;
-});
\ No newline at end of file
+});
diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
index 597072c..ae5d6b9 100644
--- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import getdate
from frappe import _
+from frappe.utils import getdate
+
def execute(filters=None):
data = get_data(filters)
@@ -165,4 +167,4 @@
if not year_list:
year_list = [getdate().year]
- return "\n".join(str(year) for year in year_list)
\ No newline at end of file
+ return "\n".join(str(year) for year in year_list)
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 daa6976..e19aeaa 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
@@ -1,21 +1,23 @@
# coding=utf-8
from __future__ import unicode_literals
-import erpnext
-import frappe
from unittest import TestCase
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
+import frappe
+
+import erpnext
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
-from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.regional.report.uae_vat_201.uae_vat_201 import (
- get_total_emiratewise,
- get_tourist_tax_return_total,
- get_tourist_tax_return_tax,
- get_zero_rated_total,
get_exempt_total,
- get_standard_rated_expenses_total,
get_standard_rated_expenses_tax,
+ get_standard_rated_expenses_total,
+ get_total_emiratewise,
+ get_tourist_tax_return_tax,
+ get_tourist_tax_return_total,
+ get_zero_rated_total,
)
+from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account
test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.html b/erpnext/regional/report/uae_vat_201/uae_vat_201.html
index d9b9968..7328f3f 100644
--- a/erpnext/regional/report/uae_vat_201/uae_vat_201.html
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.html
@@ -74,4 +74,4 @@
{% } %}
</tbody>
-</table>
\ No newline at end of file
+</table>
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 b061423..f4c049d 100644
--- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns()
data, emirates, amounts_by_emirate = get_data(filters)
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
new file mode 100644
index 0000000..77beff3
--- /dev/null
+++ b/erpnext/regional/report/vat_audit_report/test_vat_audit_report.py
@@ -0,0 +1,195 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+from unittest import TestCase
+
+import frappe
+from frappe.utils import today
+
+from erpnext.accounts.doctype.account.test_account import create_account
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.regional.report.vat_audit_report.vat_audit_report import execute
+
+
+class TestVATAuditReport(TestCase):
+ def setUp(self):
+ 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")
+ set_sa_vat_accounts()
+
+ make_item("_Test SA VAT Item")
+ make_item("_Test SA VAT Zero Rated Item", properties = {"is_zero_rated": 1})
+
+ make_customer()
+ make_supplier()
+
+ make_sales_invoices()
+ create_purchase_invoices()
+
+ def tearDown(self):
+ frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company SA VAT'")
+ 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()
+ }
+ 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']
+
+ 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.insert()
+ else:
+ company = frappe.get_doc("Company", company_name)
+
+ 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 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"
+ }
+ )
+
+ sa_vat_accounts = []
+ for account in vat_accounts:
+ 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()
+
+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()
+
+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()
+
+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"
+ })
+
+ 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",
+ 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
+ )
+ if tax:
+ si.append("taxes", {
+ "charge_type": "On Net Total",
+ "account_head": tax_account,
+ "cost_center": "Main - _TCSV",
+ "description": "VAT 15% @ 15.0",
+ "rate": tax_rate
+ })
+
+ si.submit()
+
+ test_item = "_Test SA VAT Item"
+ test_zero_rated_item = "_Test SA VAT Zero Rated Item"
+
+ 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
+ )
+ 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 292605e..3637bca 100644
--- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py
+++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py
@@ -1,11 +1,14 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
from frappe import _
-from frappe.utils import formatdate
+from frappe.utils import formatdate, get_link_to_form
+
def execute(filters=None):
return VATAuditReport(filters).run()
@@ -42,7 +45,8 @@
self.sa_vat_accounts = frappe.get_list("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:
- frappe.throw(_("Please set VAT Accounts in 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()
@@ -69,7 +73,7 @@
items = frappe.db.sql("""
SELECT
- item_code, parent, taxable_value, base_net_amount, is_zero_rated
+ item_code, parent, base_net_amount, is_zero_rated
FROM
`tab%s Item`
WHERE
@@ -79,7 +83,7 @@
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('taxable_value', 0) or d.get('base_net_amount', 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):
@@ -189,7 +193,7 @@
row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy")
row["voucher_type"] = doctype
row["voucher_no"] = inv
- row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier"
+ 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")
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index 9b3677d..6113f48 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -2,10 +2,36 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
-from erpnext.regional.united_arab_emirates.setup import make_custom_fields, add_print_formats
-
+import frappe
+from frappe.permissions import add_permission, update_permission_property
+from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields, add_print_formats
+from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
def setup(company=None, patch=True):
- make_custom_fields()
+ uae_custom_fields()
add_print_formats()
+ add_permissions()
+ create_ksa_vat_setting(company)
+ make_qrcode_field()
+
+def add_permissions():
+ """Add Permissions for KSA VAT Setting."""
+ add_permission('KSA VAT Setting', 'All', 0)
+ for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+ add_permission('KSA VAT Setting', role, 0)
+ update_permission_property('KSA VAT Setting', role, 0, 'write', 1)
+ update_permission_property('KSA VAT Setting', role, 0, 'create', 1)
+
+ """Enable KSA VAT Report"""
+ frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0)
+
+def make_qrcode_field():
+ """Created QR code Image file"""
+ qr_code_field = dict(
+ fieldname='qr_code',
+ label='QR Code',
+ fieldtype='Attach Image',
+ read_only=1, no_copy=1, hidden=1)
+
+ create_custom_field('Sales Invoice', qr_code_field)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
new file mode 100644
index 0000000..cc6c0af
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -0,0 +1,77 @@
+import io
+import os
+
+import frappe
+from pyqrcode import create as qr_create
+
+from erpnext import get_region
+
+
+def create_qr_code(doc, method):
+ """Create QR Code after inserting Sales Inv
+ """
+
+ region = get_region(doc.company)
+ if region not in ['Saudi Arabia']:
+ return
+
+ # if QR Code field not present, do nothing
+ if not hasattr(doc, 'qr_code'):
+ return
+
+ # Don't create QR Code if it already exists
+ qr_code = doc.get("qr_code")
+ if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
+ return
+
+ meta = frappe.get_meta('Sales Invoice')
+
+ for field in meta.get_image_fields():
+ if field.fieldname == 'qr_code':
+ # Creating public url to print format
+ default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=doc.doctype), "value")
+
+ # System Language
+ language = frappe.get_system_settings('language')
+
+ # creating qr code for the url
+ url = f"{ frappe.utils.get_url() }/{ doc.doctype }/{ doc.name }?format={ default_print_format or 'Standard' }&_lang={ language }&key={ doc.get_signature() }"
+ qr_image = io.BytesIO()
+ url = qr_create(url, error='L')
+ url.png(qr_image, scale=2, quiet_zone=1)
+
+ # making file
+ filename = f"QR-CODE-{doc.name}.png".replace(os.path.sep, "__")
+ _file = frappe.get_doc({
+ "doctype": "File",
+ "file_name": filename,
+ "is_private": 0,
+ "content": qr_image.getvalue(),
+ "attached_to_doctype": doc.get("doctype"),
+ "attached_to_name": doc.get("name"),
+ "attached_to_field": "qr_code"
+ })
+
+ _file.save()
+
+ # assigning to document
+ doc.db_set('qr_code', _file.file_url)
+ doc.notify_update()
+
+ break
+
+
+def delete_qr_code_file(doc, method):
+ """Delete QR Code on deleted sales invoice"""
+
+ region = get_region(doc.company)
+ if region not in ['Saudi Arabia']:
+ return
+
+ if hasattr(doc, 'qr_code'):
+ if doc.get('qr_code'):
+ file_doc = frappe.get_list('File', {
+ 'file_url': doc.get('qr_code')
+ })
+ if len(file_doc):
+ frappe.delete_doc('File', file_doc[0].name)
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_user/__init__.py b/erpnext/regional/saudi_arabia/wizard/__init__.py
similarity index 100%
copy from erpnext/regional/doctype/e_invoice_user/__init__.py
copy to erpnext/regional/saudi_arabia/wizard/__init__.py
diff --git a/erpnext/regional/doctype/e_invoice_settings/__init__.py b/erpnext/regional/saudi_arabia/wizard/data/__init__.py
similarity index 100%
copy from erpnext/regional/doctype/e_invoice_settings/__init__.py
copy to erpnext/regional/saudi_arabia/wizard/data/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
new file mode 100644
index 0000000..709d65b
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
@@ -0,0 +1,47 @@
+[
+ {
+ "type": "Sales Account",
+ "accounts": [
+ {
+ "title": "Standard rated Sales",
+ "item_tax_template": "KSA VAT 5%",
+ "account": "VAT 5%"
+ },
+ {
+ "title": "Zero rated domestic sales",
+ "item_tax_template": "KSA VAT Zero",
+ "account": "VAT Zero"
+ },
+ {
+ "title": "Exempted sales",
+ "item_tax_template": "KSA VAT Exempted",
+ "account": "VAT Zero"
+ }
+ ]
+ },
+ {
+ "type": "Purchase Account",
+ "accounts": [
+ {
+ "title": "Standard rated domestic purchases",
+ "item_tax_template": "KSA VAT 5%",
+ "account": "VAT 5%"
+ },
+ {
+ "title": "Imports subject to VAT paid at customs",
+ "item_tax_template": "KSA Excise 50%",
+ "account": "Excise 50%"
+ },
+ {
+ "title": "Zero rated purchases",
+ "item_tax_template": "KSA VAT Zero",
+ "account": "VAT Zero"
+ },
+ {
+ "title": "Exempted purchases",
+ "item_tax_template": "KSA VAT Exempted",
+ "account": "VAT Zero"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_request_log/__init__.py b/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
similarity index 100%
copy from erpnext/regional/doctype/e_invoice_request_log/__init__.py
copy to erpnext/regional/saudi_arabia/wizard/operations/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
new file mode 100644
index 0000000..3c89edd
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
@@ -0,0 +1,46 @@
+import json
+import os
+
+import frappe
+
+from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
+
+
+def create_ksa_vat_setting(company):
+ """On creation of first company. Creates KSA VAT Setting"""
+
+ company = frappe.get_doc('Company', company)
+ setup_taxes_and_charges(company.name, company.country)
+
+ file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json')
+ with open(file_path, 'r') as json_file:
+ account_data = json.load(json_file)
+
+ # Creating KSA VAT Setting
+ ksa_vat_setting = frappe.get_doc({
+ 'doctype': 'KSA VAT Setting',
+ 'company': company.name
+ })
+
+ for data in account_data:
+ if data['type'] == 'Sales Account':
+ for row in data['accounts']:
+ item_tax_template = row['item_tax_template']
+ account = row['account']
+ ksa_vat_setting.append('ksa_vat_sales_accounts', {
+ 'title': row['title'],
+ 'item_tax_template': f'{item_tax_template} - {company.abbr}',
+ 'account': f'{account} - {company.abbr}'
+ })
+
+ elif data['type'] == 'Purchase Account':
+ for row in data['accounts']:
+ item_tax_template = row['item_tax_template']
+ account = row['account']
+ ksa_vat_setting.append('ksa_vat_purchase_accounts', {
+ 'title': row['title'],
+ 'item_tax_template': f'{item_tax_template} - {company.abbr}',
+ 'account': f'{account} - {company.abbr}'
+ })
+
+ ksa_vat_setting.save()
diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py
index 4657ff8..8a75987 100644
--- a/erpnext/regional/south_africa/setup.py
+++ b/erpnext/regional/south_africa/setup.py
@@ -24,7 +24,7 @@
'Sales Invoice Item': is_zero_rated,
'Purchase Invoice Item': is_zero_rated
}
-
+
create_custom_fields(custom_fields, update=update)
def add_permissions():
@@ -36,7 +36,7 @@
add_permission(doctype, role, 0)
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(
@@ -47,4 +47,4 @@
dict(role='Accounts Manager'),
dict(role='Auditor')
]
- )).insert()
\ No newline at end of file
+ )).insert()
diff --git a/erpnext/regional/turkey/setup.py b/erpnext/regional/turkey/setup.py
index ebf3b2b..2396aab 100644
--- a/erpnext/regional/turkey/setup.py
+++ b/erpnext/regional/turkey/setup.py
@@ -1,4 +1,4 @@
from __future__ import unicode_literals
def setup(company=None, patch=True):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py
index bd12d66..8b19050 100644
--- a/erpnext/regional/united_arab_emirates/setup.py
+++ b/erpnext/regional/united_arab_emirates/setup.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
-import frappe, os, json
+import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.permissions import add_permission, update_permission_property
from erpnext.payroll.doctype.gratuity_rule.gratuity_rule import get_gratuity_rule
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index 7d5fd6e..66a9651 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -1,11 +1,14 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
-import erpnext
-from frappe.utils import flt, round_based_on_smallest_currency_fraction, money_in_words
-from erpnext.controllers.taxes_and_totals import get_itemised_tax
+from frappe.utils import flt, money_in_words, round_based_on_smallest_currency_fraction
from six import iteritems
+import erpnext
+from erpnext.controllers.taxes_and_totals import get_itemised_tax
+
+
def update_itemised_tax_data(doc):
if not doc.taxes: return
diff --git a/erpnext/regional/united_states/product_tax_category_data.json b/erpnext/regional/united_states/product_tax_category_data.json
new file mode 100644
index 0000000..4527bb2
--- /dev/null
+++ b/erpnext/regional/united_states/product_tax_category_data.json
@@ -0,0 +1,4084 @@
+{
+ "categories": [
+ {
+ "description": "An item commonly used by a student in a course of study. This category is limited to the following items...binders, blackboard chalk, cellophane tape, compasses, composition books, crayons, erasers, folders, glue/paste/glue sticks, highlighters, index cards, index card boxes, legal pads, lunch boxes, markers, notebooks, paper (copy, graph, tracing, manila, colored, construction, notebook), pencils, pencil boxes, pencil sharpeners, pens, posterboard, protractors, rulers, scissors, writing tablets.",
+ "name": "School Supplies",
+ "product_tax_code": "44121600A0001"
+ },
+ {
+ "description": "This is a labor charge for: the planning and design of interior spaces; preparation of layout drawings, schedules, and specifications pertaining to the planning and design of interior spaces; furniture arranging; design and planning of furniture, fixtures, and cabinetry; staging; lighting and sound design; and the selection, purchase, and arrangement of surface coverings, draperies, furniture, and other decorations.",
+ "name": "Interior Decorating Services",
+ "product_tax_code": "73890600A0000"
+ },
+ {
+ "description": "Ammunition for firearms with a barrel greater than an internal diameter of .50 caliber or a shotgun larger than 10 gauge., including bullets, shotgun shells, and gunpowder.",
+ "name": "Ammunition - designed for firearms other than small arms.",
+ "product_tax_code": "46101600A0002"
+ },
+ {
+ "description": "Firearms, limited to pistols, revolvers, rifles with a barrel greater than an internal diameter of .50 caliber or a shotgun larger than 10 gauge.",
+ "name": "Firearms - other than small arms",
+ "product_tax_code": "46101500A0002"
+ },
+ {
+ "description": "A charge for an objective visual examination of a house’s systems and physical structure. The charge includes a report of the inspector's findings including pictures, analysis, and recommendations.",
+ "name": "Home Inspection Services",
+ "product_tax_code": "80131802A0001"
+ },
+ {
+ "description": "A charge for custodial services to residential structures, including the cleaning of floors, carpets, walls, windows, appliances, furniture, fixtures, exterior cleaning, etc. No Tangible Personal Property is transferred.",
+ "name": "Cleaning/Janitorial Services - Residential",
+ "product_tax_code": "76111501A0001"
+ },
+ {
+ "description": "A subscription service for membership to an online dating platform.",
+ "name": "Online Dating Services",
+ "product_tax_code": "91000000A1111"
+ },
+ {
+ "description": "A charge for the service to maintain the proper operation of home or building gutters through cleaning out debris that could otherwise affect the proper water flow through the gutter system.",
+ "name": "Gutter Cleaning Services",
+ "product_tax_code": "72152602A0001"
+ },
+ {
+ "description": "Clothing - Swim Fins",
+ "name": "Clothing - Swim Fins",
+ "product_tax_code": "4914606A0001"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods can be streamed and/or downloaded to a device with permanent access granted. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - bundle - downloaded with permanent rights and streamed - non subscription",
+ "product_tax_code": "55111516A0110"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods can be streamed and/or downloaded to a device with access that expires after a stated period of time. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - bundle - downloaded with limited rights and streamed - non subscription",
+ "product_tax_code": "55111516A0210"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods are downloaded to a device with access that expires after a stated period of time. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - downloaded - non subscription - with limited rights",
+ "product_tax_code": "55111516A0020"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods are downloaded to a device with permanent access granted. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - downloaded - non subscription - with permanent rights",
+ "product_tax_code": "55111516A0010"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods are streamed to a device with access that expires after a stated period of time. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - streamed - non subscription - with limited rights",
+ "product_tax_code": "55111516A0030"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods are streamed to a device with access that is conditioned upon continued subscription payment. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - streamed - subscription - with conditional rights",
+ "product_tax_code": "55111516A0040"
+ },
+ {
+ "description": "A series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any. These goods are streamed and/or downloaded to a device with access that is conditioned upon continued subscription payment. These goods include motion pictures, music videos, animations, news and entertainment programs, and live events, but do not include video greeting cards or video or electronic games.",
+ "name": "Digital Audio Visual Works - bundle - downloaded and streamed - subscription - with conditional rights",
+ "product_tax_code": "55111516A0310"
+ },
+ {
+ "description": "Works that result from the fixation of a series of musical, spoken, or other sounds that are transferred electronically. These goods are streamed to a device with access that is conditioned upon continued subscription payment. These goods include prerecorded or live music, prerecorded or live readings of books or other written materials, prerecorded or live speeches, ringtones, or other sound recordings, but not including audio greeting cards.",
+ "name": "Digital Audio Works - streamed - subscription - with conditional rights",
+ "product_tax_code": "55111512A0040"
+ },
+ {
+ "description": "Works that result from the fixation of a series of musical, spoken, or other sounds that are transferred electronically. These goods are streamed to a device with access that expires after a stated period of time. These goods include prerecorded or live music, prerecorded or live readings of books or other written materials, prerecorded or live speeches, ringtones, or other sound recordings, but not including audio greeting cards.",
+ "name": "Digital Audio Works - streamed - non subscription - with limited rights",
+ "product_tax_code": "55111512A0030"
+ },
+ {
+ "description": "Works that result from the fixation of a series of musical, spoken, or other sounds that are transferred electronically. These goods are downloaded to a device with permanent access granted. These goods include prerecorded or live music, prerecorded or live readings of books or other written materials, prerecorded or live speeches, ringtones, or other sound recordings, but not including audio greeting cards.",
+ "name": "Digital Audio Works - downloaded - non subscription - with permanent rights",
+ "product_tax_code": "55111512A0010"
+ },
+ {
+ "description": "Works that result from the fixation of a series of musical, spoken, or other sounds that are transferred electronically. These goods are downloaded to a device with access that expires after a stated period of time. These goods include prerecorded or live music, prerecorded or live readings of books or other written materials, prerecorded or live speeches, ringtones, or other sound recordings, but not including audio greeting cards.",
+ "name": "Digital Audio Works - downloaded - non subscription - with limited rights",
+ "product_tax_code": "55111512A0020"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals with the entire publication or individual articles viewable (but not downloadable) on a device with access that is conditioned upon continued subscription payment.",
+ "name": "Digital Newspapers - viewable only - subscription - with conditional rights",
+ "product_tax_code": "55111507A0060"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals with the entire publication or individual articles viewable (but not downloadable) on a device with permanent access granted. The publication is accessed without a subscription.",
+ "name": "Digital Newspapers - viewable only - non subscription - with permanent rights",
+ "product_tax_code": "55111507A0040"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals with the entire publication or individual articles viewable (but not downloadable) on a device with access that expires after a stated period of time. The publication is accessed without a subscription.",
+ "name": "Digital Newspapers - viewable only - non subscription - with limited rights",
+ "product_tax_code": "55111507A0030"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals. The publication is accessed via a subscription which also entitles the purchaser to physical copies of the media.",
+ "name": "Digital Newspapers - subscription tangible and digital",
+ "product_tax_code": "55111507A0070"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals with the entire publication or individual articles downloaded to a device with access that is conditioned upon continued subscription payment.",
+ "name": "Digital Newspapers - downloadable - subscription - with conditional rights",
+ "product_tax_code": "55111507A0050"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals with the entire publication or individual articles downloaded to a device with permanent access granted. The publication is accessed without a subscription.",
+ "name": "Digital Newspapers - downloadable - non subscription - with permanent rights",
+ "product_tax_code": "55111507A0010"
+ },
+ {
+ "description": "A digital version of a traditional newspaper published at regular intervals with the entire publication or individual articles downloaded to a device with access that expires after a stated period of time. The publication is accessed without a subscription.",
+ "name": "Digital Newspapers - downloadable - non subscription - with limited rights",
+ "product_tax_code": "55111507A0020"
+ },
+ {
+ "description": "A digital version of a traditional periodical published at regular intervals with the entire publication or individual articles viewable (but not downloadable) on a device with access that is conditioned upon continued subscription payment.",
+ "name": "Digital Magazines/Periodicals - viewable only - subscription - with conditional rights",
+ "product_tax_code": "55111506A0060"
+ },
+ {
+ "description": "A digital version of a traditional periodical published at regular intervals with the entire publication or individual articles viewable (but not downloadable) on a device with permanent access granted. The publication is accessed without a subscription.",
+ "name": "Digital Magazines/Periodicals - viewable only - non subscription - with permanent rights",
+ "product_tax_code": "55111506A0040"
+ },
+ {
+ "description": "A digital version of a traditional periodical published at regular intervals with the entire publication or individual articles viewable (but not downloadable) on a device with access that expires after a stated period of time. The publication is accessed without a subscription.",
+ "name": "Digital Magazines/Periodicals - viewable only - non subscription - with limited rights",
+ "product_tax_code": "55111506A0030"
+ },
+ {
+ "description": "A digital version of a traditional magazine published at regular intervals. The publication is accessed via a subscription which also entitles the purchaser to physical copies of the media.",
+ "name": "Digital Magazines/Periodicals - subscription tangible and digital",
+ "product_tax_code": "55111506A0070"
+ },
+ {
+ "description": "A digital version of a traditional periodical published at regular intervals with the entire publication or individual articles downloaded to a device with access that is conditioned upon continued subscription payment.",
+ "name": "Digital Magazines/Periodicals - downloadable - subscription - with conditional rights",
+ "product_tax_code": "55111506A0050"
+ },
+ {
+ "description": "A digital version of a traditional periodical published at regular intervals with the entire publication or individual articles downloaded to a device with permanent access granted. The publication is accessed without a subscription.",
+ "name": "Digital Magazines/Periodicals - downloadable - non subscription - with permanent rights",
+ "product_tax_code": "55111506A0010"
+ },
+ {
+ "description": "A digital version of a traditional periodical published at regular intervals with the entire publication or individual articles downloaded to a device with access that expires after a stated period of time. The publication is accessed without a subscription.",
+ "name": "Digital Magazines/Periodicals - downloadable - non subscription - with limited rights",
+ "product_tax_code": "55111506A0020"
+ },
+ {
+ "description": "Works that are generally recognized in the ordinary and usual sense as books and are transferred electronically. These goods are viewable (but not downloadable) on a device with access that is conditioned upon continued subscription payment. These goods include novels, autobiographies, encyclopedias, dictionaries, repair manuals, phone directories, business directories, zip code directories, cookbooks, etc.",
+ "name": "Digital Books - viewable only - subscription - with conditional rights",
+ "product_tax_code": "55111505A0060"
+ },
+ {
+ "description": "Works that are generally recognized in the ordinary and usual sense as books and are transferred electronically. These goods are downloaded to a device with access that is conditioned upon continued subscription payment. These goods include novels, autobiographies, encyclopedias, dictionaries, repair manuals, phone directories, business directories, zip code directories, cookbooks, etc.",
+ "name": "Digital Books - downloaded - subscription - with conditional rights",
+ "product_tax_code": "55111505A0050"
+ },
+ {
+ "description": "Works that are generally recognized in the ordinary and usual sense as books and are transferred electronically. These goods are downloaded to a device with permanent access granted. These goods include novels, autobiographies, encyclopedias, dictionaries, repair manuals, phone directories, business directories, zip code directories, cookbooks, etc.",
+ "name": "Digital Books - downloaded - non subscription - with permanent rights",
+ "product_tax_code": "55111505A0010"
+ },
+ {
+ "description": "Works that are generally recognized in the ordinary and usual sense as books and are transferred electronically. These goods are downloaded to a device with access that expires after a stated period of time. These goods include novels, autobiographies, encyclopedias, dictionaries, repair manuals, phone directories, business directories, zip code directories, cookbooks, etc.",
+ "name": "Digital Books - downloaded - non subscription - with limited rights",
+ "product_tax_code": "55111505A0020"
+ },
+ {
+ "description": "The final art used for actual reproduction by photomechanical or other processes or for display purposes, but does not include website or home page design, and that is transferred electronically. These goods are downloaded to a device with access that is conditioned upon continued subscription payment. These goods include drawings, paintings, designs, photographs, lettering, paste-ups, mechanicals, assemblies, charts, graphs, illustrative materials, etc.",
+ "name": "Digital Finished Artwork - downloaded - subscription - with conditional rights",
+ "product_tax_code": "82141502A0050"
+ },
+ {
+ "description": "The final art used for actual reproduction by photomechanical or other processes or for display purposes, but does not include website or home page design, and that is transferred electronically. These goods are downloaded to a device with permanent access granted. These goods include drawings, paintings, designs, photographs, lettering, paste-ups, mechanicals, assemblies, charts, graphs, illustrative materials, etc.",
+ "name": "Digital Finished Artwork - downloaded - non subscription - with permanent rights",
+ "product_tax_code": "82141502A0010"
+ },
+ {
+ "description": "The final art used for actual reproduction by photomechanical or other processes or for display purposes, but does not include website or home page design, and that is transferred electronically. These goods are downloaded to a device with access that expires after a stated period of time. These goods include drawings, paintings, designs, photographs, lettering, paste-ups, mechanicals, assemblies, charts, graphs, illustrative materials, etc.",
+ "name": "Digital Finished Artwork - downloaded - non subscription - with limited rights",
+ "product_tax_code": "82141502A0020"
+ },
+ {
+ "description": "Individual digital news articles, newsletters, and other stand-alone documents. These goods are viewable (but not downloadable) on a device with access that is conditioned upon continued subscription payment.",
+ "name": "Digital other news or documents - viewable only - subscription - with conditional rights",
+ "product_tax_code": "82111900A0002"
+ },
+ {
+ "description": "Individual digital news articles, newsletters, and other stand-alone documents. These goods are viewable (but not downloadable) on a device with permanent access granted.",
+ "name": "Digital other news or documents - viewable only - non subscription - with permanent rights",
+ "product_tax_code": "82111900A0005"
+ },
+ {
+ "description": "Individual digital news articles, newsletters, and other stand-alone documents. These goods are viewable (but not downloadable) on a device with access that expires after a stated period of time.",
+ "name": "Digital other news or documents - viewable only - non subscription - with limited rights",
+ "product_tax_code": "82111900A0006"
+ },
+ {
+ "description": "Individual digital news articles, newsletters, and other stand-alone documents. These goods are downloaded to a device with access that is conditioned upon continued subscription payment.",
+ "name": "Digital other news or documents - downloadable - subscription - with conditional rights",
+ "product_tax_code": "82111900A0001"
+ },
+ {
+ "description": "Individual digital news articles, newsletters, and other stand-alone documents. These goods are downloaded to a device with permanent access granted. These publications are accessed without a subscription.",
+ "name": "Digital other news or documents - downloadable - non subscription - with permanent rights",
+ "product_tax_code": "82111900A0003"
+ },
+ {
+ "description": "Individual digital news articles, newsletters, and other stand-alone documents. These goods are downloaded to a device with access that expires after a stated period of time.",
+ "name": "Digital other news or documents - downloadable - non subscription - with limited rights",
+ "product_tax_code": "82111900A0004"
+ },
+ {
+ "description": "Works that are required as part of a formal academic education program and are transferred electronically. These goods are downloaded to a device with permanent access granted.",
+ "name": "Digital School Textbooks - downloaded - non subscription - with permanent rights",
+ "product_tax_code": "55111513A0010"
+ },
+ {
+ "description": "Works that are required as part of a formal academic education program and are transferred electronically. These goods are downloaded to a device with access that expires after a stated period of time.",
+ "name": "Digital School Textbooks - downloaded - non subscription - with limited rights",
+ "product_tax_code": "55111513A0020"
+ },
+ {
+ "description": "An electronic greeting \"card\" typically sent via email that contains only static images or text, rather than an audio visual or audio only experience.",
+ "name": "Digital Greeting Cards - Static text and/or images only",
+ "product_tax_code": "14111605A0003"
+ },
+ {
+ "description": "An electronic greeting \"card\" typically sent via email that contains a series of related images which, when shown in succession, impart an impression of motion, together with accompanying sounds, if any.",
+ "name": "Digital Greeting Cards - Audio Visual",
+ "product_tax_code": "14111605A0001"
+ },
+ {
+ "description": "An electronic greeting \"card\" typically sent via email that contains an audio only message.",
+ "name": "Digital Greeting Cards - Audio Only",
+ "product_tax_code": "14111605A0002"
+ },
+ {
+ "description": "Digital images that are downloaded to a device with permanent access granted.",
+ "name": "Digital Photographs/Images - downloaded - non subscription - with permanent rights for permanent use",
+ "product_tax_code": "60121011A0001"
+ },
+ {
+ "description": "Video or electronic games in the common sense are transferred electronically. These goods are streamed to a device with access that is conditioned upon continued subscription payment.",
+ "name": "Video Games - streamed - subscription - with conditional rights",
+ "product_tax_code": "60141104A0040"
+ },
+ {
+ "description": "Video or electronic games in the common sense are transferred electronically. These goods are streamed to a device with access that expires after a stated period of time.",
+ "name": "Video Games - streamed - non subscription - with limited rights",
+ "product_tax_code": "60141104A0030"
+ },
+ {
+ "description": "Video or electronic games in the common sense are transferred electronically. These goods are downloaded to a device with access that is conditioned upon continued subscription payment.",
+ "name": "Video Games - downloaded - subscription - with conditional rights",
+ "product_tax_code": "60141104A0050"
+ },
+ {
+ "description": "Video or electronic games in the common sense are transferred electronically. These goods are downloaded to a device with permanent access granted.",
+ "name": "Video Games - downloaded - non subscription - with permanent rights",
+ "product_tax_code": "60141104A0010"
+ },
+ {
+ "description": "Video or electronic games in the common sense are transferred electronically. These goods are downloaded to a device with access that expires after a stated period of time.",
+ "name": "Video Games - downloaded - non subscription - with limited rights",
+ "product_tax_code": "60141104A0020"
+ },
+ {
+ "description": "The conceptualize, design, program or maintain a website. The code is unique to a particular client's website.",
+ "name": "Web Site Design",
+ "product_tax_code": "81112103A0000"
+ },
+ {
+ "description": "The process of renting or buying space to house a website on the World Wide Web. Website content such as HTML, CSS, and images has to be housed on a server to be viewable online.",
+ "name": "Web Hosting Services",
+ "product_tax_code": "81112105A0000"
+ },
+ {
+ "description": "A charge separately stated from the sale of the product itself that entitles the purchaser to future repair and labor services to return the defective item of tangible personal property to its original state. The warranty contract is optional to the purchaser. Motor vehicle warranties are excluded.",
+ "name": "Warranty - Optional",
+ "product_tax_code": "81111818A0000"
+ },
+ {
+ "description": "A charge separately stated from the sale of the product itself that entitles the purchaser to future repair and labor services to return the defective item of tangible personal property to its original state. The warranty contract is mandatory and is required to be purchased on conjunction with the purchased tangible personal property. Motor vehicle warranties are excluded.",
+ "name": "Warranty - Mandatory",
+ "product_tax_code": "81111818A0001"
+ },
+ {
+ "description": "Personal or small group teaching, designed to help people who need extra help with their studies",
+ "name": "Tutoring",
+ "product_tax_code": "86132001A0000"
+ },
+ {
+ "description": "Self Study web based training, not instructor led. This does not include downloads of video replays.",
+ "name": "Training Services - Self Study Web Based",
+ "product_tax_code": "86132000A0002"
+ },
+ {
+ "description": "Live training web based. This does not include video replays of the instruction or course.",
+ "name": "Training Services - Live Virtual",
+ "product_tax_code": "86132201A0000"
+ },
+ {
+ "description": "Charges for installing, configuring, debugging, modifying, testing, or troubleshooting computer hardware, networks, programs or software. Labor only charge.",
+ "name": "Technical Support Services",
+ "product_tax_code": "81111811A0001"
+ },
+ {
+ "description": "A charge to preserve an animal's body via mounting or stuffing, for the purpose of display or study. The customer provide the animal. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Taxidermy Services",
+ "product_tax_code": "82151508A0000"
+ },
+ {
+ "description": "A charge to have files or documents shredded either onsite or offsite.",
+ "name": "Shredding Service",
+ "product_tax_code": "44101603A9007"
+ },
+ {
+ "description": "A charge to repair or restore footwear was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential. Note: This product tax code will partially apply tax in CA, MI, IL.",
+ "name": "Shoe Repair",
+ "product_tax_code": "53111600A9007"
+ },
+ {
+ "description": "A charge for the printing, imprinting, lithographing, mimeographing, photocopying, and similar reproductions of various articles including mailers, catalogs, letterhead, envelopes, business cards, presentation folders, forms, signage, etc. The end result is the transfer of tangible personal property to the customer.",
+ "name": "Printing",
+ "product_tax_code": "82121500A0000"
+ },
+ {
+ "description": "Service processing payroll checks and tracking payroll data; including printing employees’ payroll checks, pay statements, management reports, tracking payroll taxes, preparing tax returns and producing W-2’s for distribution.",
+ "name": "Payroll Services",
+ "product_tax_code": "87210202A0000"
+ },
+ {
+ "description": "A charge to repair or restore to operating condition a motor vehicle that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Motor Vehicle Repair",
+ "product_tax_code": "25100000A9007"
+ },
+ {
+ "description": "A charge to make customer provided meat suitable for human consumption, typically referred to a butcher or slaughter services.",
+ "name": "Meat Processing",
+ "product_tax_code": "42447000A0000"
+ },
+ {
+ "description": "A charge to repair or restore to operating condition a machine that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Machine Repair",
+ "product_tax_code": "23019007A0000"
+ },
+ {
+ "description": "A charge to provide laundry services to linens and the like. This charge is not for clothing items. The business customer is the owner of the items being cleaned.",
+ "name": "Linen Services - Laundry only - items other than clothing",
+ "product_tax_code": "91111502A1601"
+ },
+ {
+ "description": "A charge to provide laundry services to clothing. The business customer is the owner of the items being cleaned.",
+ "name": "Linen Services - Laundry only",
+ "product_tax_code": "91111502A1600"
+ },
+ {
+ "description": "A charge to repair or restore jewelry that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Jewelry Repair",
+ "product_tax_code": "54119007A0000"
+ },
+ {
+ "description": "A charge for the wrapping of articles in a box or bag with paper and other decorative additions. The wrapping not linked the purchased of the article(s) and is performed by a party other vendor of the article(s).",
+ "name": "Gift Wrapping - separate from purchase of article",
+ "product_tax_code": "14111601A9007"
+ },
+ {
+ "description": "A charge for the wrapping of articles in a box or bag with paper and other decorative additions. The charge is separately stated from the article.",
+ "name": "Gift Wrapping - in conjunction with purchase of article",
+ "product_tax_code": "14111601A0001"
+ },
+ {
+ "description": "A charge to perform an alteration on a item of clothing by a service provider other than vendor of the article. The alteration is not linked to the clothing purchase. Alterations could include hemming of a dress, shortening of pants, adjusting the waistline of a garment, etc.",
+ "name": "Garment Alterations- separate from purchase of garment",
+ "product_tax_code": "81149000A0000"
+ },
+ {
+ "description": "A charge to perform an alteration on a item of clothing by the vendor of the article. The alteration is separately stated from the clothing, but contracted for at the time of the clothing purchase. Alterations could include hemming of a dress, shortening of pants, adjusting the waistline of a garment, etc.",
+ "name": "Garment Alterations- in conjunction with purchase of garment",
+ "product_tax_code": "81149000A0001"
+ },
+ {
+ "description": "A separately stated labor charge to cover a piece of furniture previously owned by the customer with new fabric coverings. Any materials transferred as part of the service are separately stated.",
+ "name": "Furniture Reupholstering",
+ "product_tax_code": "72153614A0000"
+ },
+ {
+ "description": "A charge to create a finished good from materials supplied by the customer. This is a labor only charge to transform a customer's existing property.",
+ "name": "Fabrication",
+ "product_tax_code": "23839000A0000"
+ },
+ {
+ "description": "E-file services for tax returns",
+ "name": "Electronic Filing Service",
+ "product_tax_code": "72910000A0000"
+ },
+ {
+ "description": "Private schools, not college or university",
+ "name": "Educational Services",
+ "product_tax_code": "86132209A0000"
+ },
+ {
+ "description": "A charge to a non-commercial customer for the cleaning or renovating items other than clothing by immersion and agitation, spraying, vaporization, or immersion only, in a volatile, commercially moisture-free solvent or by the use of a volatile or inflammable product. This does not include the use of a self-service coin (or credit card) operated cleaning machine.",
+ "name": "Dry Cleaning - items other than clothing",
+ "product_tax_code": "91111503A1601"
+ },
+ {
+ "description": "A charge to repair or restore to operating condition computer hardware that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Computer Repair",
+ "product_tax_code": "81112300A0000"
+ },
+ {
+ "description": "A charge to clean, wash or wax a motor vehicle, other than a self-service coin (or credit card) operated washing station. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Car Washing",
+ "product_tax_code": "81119200A0000"
+ },
+ {
+ "description": "A charge to assemble goods for a purchaser who will later sell the assembled goods to end consumers.",
+ "name": "Assembly - prior to final purchase of article",
+ "product_tax_code": "93121706A0001"
+ },
+ {
+ "description": "A charge separately stated from the sale of the product itself to bring the article to its finished state and in the condition specified by the buyer.",
+ "name": "Assembly - in conjunction with final purchase of article",
+ "product_tax_code": "93121706A0000"
+ },
+ {
+ "description": "A charge to repair or restore to operating condition an appliance (dishwasher, washing machine, refrigerator, etc.) that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Appliance Repair",
+ "product_tax_code": "52143609A0000"
+ },
+ {
+ "description": "A charge to repair or restore to operating condition an aircraft that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential. Commercial aircraft is excluded.",
+ "name": "Aircraft Repair",
+ "product_tax_code": "78181800A0000"
+ },
+ {
+ "description": "A charge for the printing, imprinting, or lithographing on any article supplied by the customer. The customer owns the article throughout the process. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Printing - customer supplied articles",
+ "product_tax_code": "19009"
+ },
+ {
+ "description": "A charge to a non-commercial customer for the cleaning or renovating clothing by immersion and agitation, spraying, vaporization, or immersion only, in a volatile, commercially moisture-free solvent or by the use of a volatile or inflammable product. This does not include the use of a self-service coin (or credit card) operated cleaning machine.",
+ "name": "Dry Cleaning Services",
+ "product_tax_code": "19006"
+ },
+ {
+ "description": "A charge to repair or restore tangible personal property that was broken, worn, damaged, defective, or malfunctioning. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Repair Services",
+ "product_tax_code": "19007"
+ },
+ {
+ "description": "Food for household pets that is consumed for nutritional value. This code is not intended for food related to working farm animals or animals raised for meat or milk production. This code is intended for retail sales made directly to end consumers.",
+ "name": "OTC Pet Food",
+ "product_tax_code": "10122100A0000"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with candy, with the candy comprising between 25% and 49% of the overall value of the bundle (food comprises 51 to 75%). Note that any candy containing flour should be considered as food (and not candy) when determining bundle percentages.",
+ "name": "Food/Candy Bundle - with Candy 25% to 49%",
+ "product_tax_code": "50193400A0008"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with candy, with the candy comprising between 11% and 24% of the overall value of the bundle (food comprises 76% to 89%). Note that any candy containing flour should be considered as food (and not candy) when determining bundle percentages.",
+ "name": "Food/Candy Bundle - with Candy 11% to 24%",
+ "product_tax_code": "50193400A0009"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with candy, with the candy comprising 10% or less of the overall value of the bundle (food comprises 90% or more). Note that any candy containing flour should be considered as food (and not candy) when determining bundle percentages.",
+ "name": "Food/Candy Bundle - with Candy 10% or less",
+ "product_tax_code": "50193400A0010"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with candy, with the candy comprising 50% or more of the overall value of the bundle (food comprises 50% or less). Note that any candy containing flour should be considered as food (and not candy) when determining bundle percentages.",
+ "name": "Food/Candy Bundle - with Candy 50% or more",
+ "product_tax_code": "50193400A0007"
+ },
+ {
+ "description": "Male or female condoms and vaginal sponges used to prevent pregnancy and/or exposure to STDs, containing a spermicidal lubricant as indicated by a \"drug facts\" panel or a statement of active ingredients, sold under prescription order of a licensed professional.",
+ "name": "Condoms with Spermicide with Prescription",
+ "product_tax_code": "53131622A0004"
+ },
+ {
+ "description": "Over-the-Counter emergency contraceptive pills act to prevent pregnancy after intercourse. The contraceptive contains a hormone that prevents ovulation, fertilization, or implantation of an embryo.",
+ "name": "Birth Control - Over-the-Counter Oral Contraceptives",
+ "product_tax_code": "51350000A0001"
+ },
+ {
+ "description": "An oral medication containing hormones effective in altering the menstrual cycle to eliminate ovulation and prevent pregnancy, available only under prescription order of a licensed professional. Other than preventing pregnancy, hormonal birth control can also be used to treat various conditions, such as Polycystic Ovary Syndrome, Endometriosis, Primary Ovarian Insufficiency, etc.",
+ "name": "Birth Control - Prescription Oral Contraceptives",
+ "product_tax_code": "51350000A0000"
+ },
+ {
+ "description": "Over-the-Counter emergency contraceptive pills act to prevent pregnancy after intercourse, sold under prescription order of a licensed professional. The contraceptive contains a hormone that prevents ovulation, fertilization, or implantation of an embryo.",
+ "name": "Birth Control - Over-the-Counter Oral Contraceptives with Prescription",
+ "product_tax_code": "51350000A0002"
+ },
+ {
+ "description": "Barrier based prescription only birth control methods, including the diaphragm and cervical cap that prevent the joining of the sperm and egg, available only under prescription order of a licensed professional.",
+ "name": "Birth Control - Prescription non-Oral Contraceptives - Barriers",
+ "product_tax_code": "42143103A0000"
+ },
+ {
+ "description": "Hormonal based birth control methods other than the oral pill, including intrauterine devices, injections, skin implants, transdermal patches, and vaginal rings that release a continuous dose of hormones to eliminate ovulation and prevent pregnancy, available only under prescription order of a licensed professional.",
+ "name": "Birth Control - Prescription non-Oral Contraceptives - Hormonal",
+ "product_tax_code": "51350000A0003"
+ },
+ {
+ "description": "Male or female condoms and vaginal sponges used to prevent pregnancy and/or exposure to STDs, sold under prescription order of a licensed professional.",
+ "name": "Condoms with Prescription",
+ "product_tax_code": "53131622A0003"
+ },
+ {
+ "description": "Feminine hygiene product designed to absorb the menstrual flow. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Tampons, menstrual cups, pads, liners",
+ "product_tax_code": "53131615A0000"
+ },
+ {
+ "description": "Infant washable/reusable cloth diapers.",
+ "name": "Clothing - Cloth Diapers",
+ "product_tax_code": "53102305A0001"
+ },
+ {
+ "description": "Clothing - Diaper liners",
+ "name": "Clothing - Diaper liners",
+ "product_tax_code": "53102308A0000"
+ },
+ {
+ "description": "Clothing - Adult diapers",
+ "name": "Clothing - Adult diapers",
+ "product_tax_code": "53102306A0000"
+ },
+ {
+ "description": "Clothing - Infant diapers",
+ "name": "Clothing - Infant diapers",
+ "product_tax_code": "53102305A0000"
+ },
+ {
+ "description": "Food and Beverage - Candy containing flour as an ingredient",
+ "name": "Food and Beverage - Candy containing flour as an ingredient",
+ "product_tax_code": "50161800A0001"
+ },
+ {
+ "description": "Food and Beverage - Food and Food Ingredients for Home Consumption",
+ "name": "Food and Beverage - Food and Food Ingredients for Home Consumption",
+ "product_tax_code": "50000000A0000"
+ },
+ {
+ "description": "Clothing - Zippers",
+ "name": "Clothing - Zippers",
+ "product_tax_code": "53141503A0000"
+ },
+ {
+ "description": "Clothing - Gorgets",
+ "name": "Clothing - Gorgets",
+ "product_tax_code": "53102519A0000"
+ },
+ {
+ "description": "Clothing - Shoulder boards or epaulettes",
+ "name": "Clothing - Shoulder boards or epaulettes",
+ "product_tax_code": "53102520A0000"
+ },
+ {
+ "description": "Yarn - For use other than fabricating/repairing clothing",
+ "name": "Yarn - For use other than fabricating/repairing clothing",
+ "product_tax_code": "11151700A0001"
+ },
+ {
+ "description": "Clothing - Snaps",
+ "name": "Clothing - Snaps",
+ "product_tax_code": "53141506A0000"
+ },
+ {
+ "description": "Clothing - Clasps",
+ "name": "Clothing - Clasps",
+ "product_tax_code": "53141507A0000"
+ },
+ {
+ "description": "Clothing - Buttons",
+ "name": "Clothing - Buttons",
+ "product_tax_code": "53141505A0000"
+ },
+ {
+ "description": "Clothing - Clothing - Fabric dye",
+ "name": "Clothing - Fabric dye",
+ "product_tax_code": "60105810A0000"
+ },
+ {
+ "description": "Clothing - Fabric for use in clothing",
+ "name": "Clothing - Fabric for use in clothing",
+ "product_tax_code": "11160000A0000"
+ },
+ {
+ "description": "Clothing - Clothing - Yarn",
+ "name": "Clothing - Yarn",
+ "product_tax_code": "11151700A0000"
+ },
+ {
+ "description": "A lump sum charge where both the downloaded digital products and the service components each are greater than 10% of the bundle.",
+ "name": "Digital Products (> 10%) / General Services (> 10%) Bundle",
+ "product_tax_code": "55111500A5000"
+ },
+ {
+ "description": "Services provided by a licensed or registered professional in the medical field. Examples: Doctor, dentist, nurse, optometrist, etc.",
+ "name": "Medical Professional Services - Physician, Dentist, and similar",
+ "product_tax_code": "62139900A0000"
+ },
+ {
+ "description": "The puncturing or penetration of the skin of a person and the insertion of jewelry or other adornment into the opening.",
+ "name": "Body Piercing",
+ "product_tax_code": "72990190A0000"
+ },
+ {
+ "description": "Cosmetic beauty treatment for the fingernails and toenails. Consists of filing, cutting and shaping and the application of polish.",
+ "name": "Manicure Services",
+ "product_tax_code": "72310104A0000"
+ },
+ {
+ "description": "Performing tests and research for a particular client in connection with the development of particular products, property, goods or services that the client sells to consumers in the regular course of business.",
+ "name": "Marketing Services",
+ "product_tax_code": "87420300A0000"
+ },
+ {
+ "description": "Performing surveying and mapping services of the surface of the earth, including the sea floor. These services may include surveying and mapping of areas above or below the surface of the earth, such as the creation of view easements or segregating rights in parcels of land by creating underground utility easements.",
+ "name": "Property Surveying Services",
+ "product_tax_code": "87130000A0000"
+ },
+ {
+ "description": "Providing a systematic inquiry, examination, or analysis of people, events or documents, to determine the facts of a given situation. The evaluation is submitted in the form of a report or provided as a testimony in legal proceedings. Techniques such as surveillance, background checks, computer searches, fingerprinting, lie detector services, and interviews may be used to gather the information.",
+ "name": "Private Investigator Services",
+ "product_tax_code": "73810204A0000"
+ },
+ {
+ "description": "The provision of expertise or strategic advice that is presented for consideration and decision-making.",
+ "name": "Consulting Services",
+ "product_tax_code": "87480000A0000"
+ },
+ {
+ "description": "Services relating to advocating for the passage or defeat of legislation to members or staff of the government.",
+ "name": "Lobbying Services",
+ "product_tax_code": "87439901A0000"
+ },
+ {
+ "description": "Preparation of materials, written or otherwise, that are designed to influence the general public or other groups by promoting the interests of a service recipient.",
+ "name": "Public Relations",
+ "product_tax_code": "87430000A0000"
+ },
+ {
+ "description": "Services related to the art and science of designing and building structures for human habitation or use and includes planning, providing preliminary studies, designs, specifications, working drawings and providing for general administration of construction contracts.",
+ "name": "Architectural Services",
+ "product_tax_code": "87120000A0000"
+ },
+ {
+ "description": "Services provided by a profession trained to apply physical laws and principles of engineering in the design, development, and utilization of machines, materials, instruments, structures, processes, and systems. The services involve any of the following activities: provision of advice, preparation of feasibility studies, preparation of preliminary and final plans and designs, provision of technical services during the construction or installation phase, inspection and evaluation of engineering projects, and related services.",
+ "name": "Engineering Services",
+ "product_tax_code": "87110000A0000"
+ },
+ {
+ "description": "Services that provide non-medical care and supervision for infant to school-age children or senior citizens.",
+ "name": "Childcare Services / Adultcare",
+ "product_tax_code": "83510000A0000"
+ },
+ {
+ "description": "Services provided by a facility for overnight care of an animal not related to veterinary care.",
+ "name": "Pet Services - Boarding",
+ "product_tax_code": "81291000A0001"
+ },
+ {
+ "description": "Services relating to or concerned with the law. Such services include, but are not limited to, representation by an attorney (or other person, when permitted) in an administrative or legal proceeding, legal drafting, paralegal services, legal research services, arbitration, mediation, and court reporting services.",
+ "name": "Legal Services",
+ "product_tax_code": "81110000A0000"
+ },
+ {
+ "description": "Medical procedure performed on an individual that is directed at improving the individual's appearance and that does not meaningfully promote the proper function of the body or prevent or treat illness or disease.",
+ "name": "Cosmetic Medical Procedure",
+ "product_tax_code": "80110517A0000"
+ },
+ {
+ "description": "Alarm monitoring involves people using computers, software, and telecommunications to monitor homes and businesses for break-ins, fires, and other unexpected events.",
+ "name": "Security - Alarm Services",
+ "product_tax_code": "73829901A0000"
+ },
+ {
+ "description": "Services related to protecting persons or their property, preventing the theft of goods, merchandise, or money. Responding to alarm signal device, burglar alarm, television camera, still camera, or a mechanical or electronic device installed or used to prevent or detect burglary, theft, shoplifting, pilferage, losses, or other security measures. Providing management and control of crowds for safety and protection.",
+ "name": "Security - Guard Services",
+ "product_tax_code": "73810105A0000"
+ },
+ {
+ "description": "Transporting under armed private security guard from one place to another any currency, jewels, stocks, bonds, paintings, or other valuables of any kind in a specially equipped motor vehicle that offers a high degree of security.",
+ "name": "Armored Car Services",
+ "product_tax_code": "73810101A0000"
+ },
+ {
+ "description": "Services related to providing personnel, on a temporary basis, to perform work or labor under the supervision or control of another.",
+ "name": "Temporary Help Services",
+ "product_tax_code": "73630103A0000"
+ },
+ {
+ "description": "Services employment agencies provide are finding a job for a job-seeker and finding an employee for an employer.",
+ "name": "Employment Services",
+ "product_tax_code": "73610000A0000"
+ },
+ {
+ "description": "Services to industrial, commercial or income-producing real property, such as as management, electrical, plumbing, painting and carpentry, provided to income-producing property.",
+ "name": "Building Management Services",
+ "product_tax_code": "73490000A0000"
+ },
+ {
+ "description": "Services which include, but are not limited to, editing, letter writing, proofreading, resume writing, typing or word processing. Not including court reporting and stenographic services.",
+ "name": "Secretarial Services",
+ "product_tax_code": "73389903A0000"
+ },
+ {
+ "description": "Services that include typing, taking shorthand, and taking and transcribing dictation for others for a consideration.",
+ "name": "Stenographic Services",
+ "product_tax_code": "73380200A0000"
+ },
+ {
+ "description": "A process that uses needles and colored ink to permanently put a mark or design on a person’s skin. Also applying permanent make-up, such as eyelining and other permanent colors to enhance the skin of the face, lips, eyelids, and eyebrows.",
+ "name": "Tattooing Services",
+ "product_tax_code": "72990106A0000"
+ },
+ {
+ "description": "A variety of personal services typically with the purpose of improving health, beauty and relaxation through treatments such as hair, massages and facials.",
+ "name": "Spa Services",
+ "product_tax_code": "72990201A0000"
+ },
+ {
+ "description": "Services where the use of structured touch, include holding, applying pressure, positioning, and mobilizing soft tissue of the body by manual technique. Note: This does not include medical massage prescribed by a physician.",
+ "name": "Massage Services",
+ "product_tax_code": "72990200A0000"
+ },
+ {
+ "description": "The measurement, processing and communication of financial information about economic entities including, but is not limited to, financial accounting, management accounting, auditing, cost containment and auditing services, taxation and accounting information systems; excluding general bookkeeping service.",
+ "name": "Accounting Services",
+ "product_tax_code": "87210200A0000"
+ },
+ {
+ "description": "The training of an animal to obey certain commands.",
+ "name": "Pet Services - Obedience Training",
+ "product_tax_code": "81291000A0002"
+ },
+ {
+ "description": "Credit monitoring services are companies consumers pay to keep an eye on your credit files. The services notifies one when they see activity in credit files, so one can determine if that activity is a result of action one took or possibly fraudulent",
+ "name": "Credit Monitoring Services",
+ "product_tax_code": "56145000A0000"
+ },
+ {
+ "description": "Grooming services for an animal such as haircuts, bathing, nail trimming, and flea dips.",
+ "name": "Pet Services - Grooming",
+ "product_tax_code": "81291000A0000"
+ },
+ {
+ "description": "Debt collection is when a collection agency or company tries to collect past-due debts from borrowers.",
+ "name": "Debt Collection Services",
+ "product_tax_code": "73229902A0000"
+ },
+ {
+ "description": "A service that arranges introductions, for a fee, for strangers seeking romantic partners or friends. This excludes online dating services.",
+ "name": "Dating Services",
+ "product_tax_code": "72990301A0000"
+ },
+ {
+ "description": "A service that allows merchants to accept credit cards as well as send credit card payment details to the credit card network. It then forwards the payment authorization back to the acquiring bank.",
+ "name": "Credit Card Processing Services",
+ "product_tax_code": "52232000A0000"
+ },
+ {
+ "description": "Services for artificial tanning and skin beautification.",
+ "name": "Tanning Services",
+ "product_tax_code": "72990105A0000"
+ },
+ {
+ "description": "Credit reporting agencies receive various types of information which can be included in their offerings for customers.",
+ "name": "Credit Reporting Services",
+ "product_tax_code": "73230000A0000"
+ },
+ {
+ "description": "Miscellaneous personal services are generally services that affect the appearance or comfort of people. This does not include haircutting/styling services",
+ "name": "Personal Services",
+ "product_tax_code": "72990000A0000"
+ },
+ {
+ "description": "The removal of hair from the face or body using chemicals or heat energy.",
+ "name": "Electrolysis",
+ "product_tax_code": "72310102A0000"
+ },
+ {
+ "description": "Services provided to insurance companies providing insurance to consumers. Examples are loss or damage appraisals, inspections, actuarial services, claims adjustment or processing. Investigations as excluded from this definition.",
+ "name": "Insurance Services",
+ "product_tax_code": "64110000A0000"
+ },
+ {
+ "description": "An at-home infectious disease test kit that can be sold without a prescription.",
+ "name": "Infectious Disease Test - Over-the-Counter",
+ "product_tax_code": "41116205A0005"
+ },
+ {
+ "description": "An at-home infectious disease test kit that can only be sold with a prescription.",
+ "name": "Infectious Disease Test - Prescription only",
+ "product_tax_code": "41116205A0004"
+ },
+ {
+ "description": "Online database information retrieval service (personal or individual)",
+ "name": "Online database information retrieval service (personal or individual)",
+ "product_tax_code": "81111902A0001"
+ },
+ {
+ "description": "Database information retrieval",
+ "name": "Database information retrieval (personal or individual)",
+ "product_tax_code": "81111901A0001"
+ },
+ {
+ "description": "Services provided by beauty shops and barber shops, including but not limited to haircutting, hair coloring, shampooing, blow drying, permanents, hair extensions, hair straightening, and hair restorations.",
+ "name": "Hairdressing Services",
+ "product_tax_code": "19008"
+ },
+ {
+ "description": "Professional services which are not subject to a service-specific tax levy.",
+ "name": "Professional Services",
+ "product_tax_code": "19005"
+ },
+ {
+ "description": "Online database information retrieval service",
+ "name": "Online database information retrieval service",
+ "product_tax_code": "81111902A0000"
+ },
+ {
+ "description": "Database information retrieval",
+ "name": "Database information retrieval",
+ "product_tax_code": "81111901A0000"
+ },
+ {
+ "description": "Cash donation",
+ "name": "Cash donation",
+ "product_tax_code": "14111803A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicaid, medical grade oyxgen.",
+ "name": "Medical Oxygen with Prescription billed to Medicaid",
+ "product_tax_code": "42271700A0008"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, a machine used that filters a patient's blood to remove excess water and waste products when the kidneys are damaged,",
+ "name": "Kidney Dialysis Equipment for home use without Prescription",
+ "product_tax_code": "42161800A0006"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicaid, equipment that: can withstand repeated use; is primarily and customarily used to serve a medical purpose; generally is not useful to a person in the absence of illness or injury; and is not worn in or on the body. Home use means the equipment is sold to an individual for use at home, regardless of where the individual resides. Examples include hospital beds, commode chairs, bed pans, shower and bath aids, IV poles, etc.",
+ "name": "Durable Medical Equipment for home use with Prescription reimbursed by Medicaid",
+ "product_tax_code": "42140000A0005"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicare, a machine used that filters a patient's blood to remove excess water and waste products when the kidneys are damaged, dysfunctional, or missing. The kidney dialysis machine is an artificial part which augments the natural functioning of the kidneys.",
+ "name": "Kidney Dialysis Equipment for home use with Prescription billed to Medicare",
+ "product_tax_code": "42161800A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicare, equipment that: can withstand repeated use; is primarily and customarily used to serve a medical purpose; generally is not useful to a person in the absence of illness or injury; and is not worn in or on the body. Home use means the equipment is sold to an individual for use at home, regardless of where the individual resides. Examples include hospital beds, commode chairs, bed pans, shower and bath aids, IV poles, etc.",
+ "name": "Durable Medical Equipment for home use with Prescription reimbursed by Medicare",
+ "product_tax_code": "42140000A0004"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicare, equipment used to administer oxygen directly into the lungs of the patient for the relief of conditions in which the human body experiences an abnormal deficiency or inadequate supply of oxygen. Oxygen equipment means oxygen cylinders, cylinder transport devices, including sheaths and carts, cylinder studs and support devices, regulators, flowmeters, tank wrenches, oxygen concentrators, liquid oxygen base dispensers, liquid oxygen portable dispensers, oxygen tubing, nasal cannulas, face masks, oxygen humidifiers, and oxygen fittings and accessories.",
+ "name": "Oxygen Delivery Equipment for home use with Prescription reimbursed by Medicare",
+ "product_tax_code": "42271700A0004"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, equipment that: can withstand repeated use; is primarily and customarily used to serve a medical purpose; generally is not useful to a person in the absence of illness or injury; and is not worn in or on the body. Home use means the equipment is sold to an individual for use at home, regardless of where the individual resides. Examples include hospital beds, commode chairs, bed pans, shower and bath aids, IV poles, etc.",
+ "name": "Durable Medical Equipment for home use with Prescription",
+ "product_tax_code": "42140000A0001"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicare, equipment used to administer oxygen directly into the lungs of the patient for the relief of conditions in which the human body experiences an abnormal deficiency or inadequate supply of oxygen. Oxygen equipment means oxygen cylinders, cylinder transport devices, including sheaths and carts, cylinder studs and support devices, regulators, flowmeters, tank wrenches, oxygen concentrators, liquid oxygen base dispensers, liquid oxygen portable dispensers, oxygen tubing, nasal cannulas, face masks, oxygen humidifiers, and oxygen fittings and accessories.",
+ "name": "Oxygen Delivery Equipment for home use with Prescription billed to Medicare",
+ "product_tax_code": "42271700A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicaid, medical grade oyxgen.",
+ "name": "Medical Oxygen with Prescription reimbursed by Medicaid",
+ "product_tax_code": "42271700A0010"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, medical grade oyxgen.",
+ "name": "Medical Oxygen with Prescription",
+ "product_tax_code": "42271700A0012"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, medical grade oyxgen.",
+ "name": "Medical Oxygen without Prescription",
+ "product_tax_code": "42271700A0011"
+ },
+ {
+ "description": "Equipment which is primarily and customarily used to provide or increase the ability to move from one place to another, sold without a prescription, and which is appropriate for use either in a home or a motor vehicle; Is not generally used by persons with normal mobility; and does not include any motor vehicle or equipment on a motor vehicle normally provided by a motor vehicle manufacturer. Examples include wheelchairs, crutches, canes, walkers, chair lifts, etc.",
+ "name": "Mobility Enhancing Equipment without Prescription",
+ "product_tax_code": "42211500A0006"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicaid, equipment used to administer oxygen directly into the lungs of the patient for the relief of conditions in which the human body experiences an abnormal deficiency or inadequate supply of oxygen. Oxygen equipment means oxygen cylinders, cylinder transport devices, including sheaths and carts, cylinder studs and support devices, regulators, flowmeters, tank wrenches, oxygen concentrators, liquid oxygen base dispensers, liquid oxygen portable dispensers, oxygen tubing, nasal cannulas, face masks, oxygen humidifiers, and oxygen fittings and accessories.",
+ "name": "Oxygen Delivery Equipment for home use with Prescription billed to Medicaid",
+ "product_tax_code": "42271700A0003"
+ },
+ {
+ "description": "Synthetic or animal-based insulin used as an injectible drug for diabetes patients, sold under prescription order of a licensed professional.",
+ "name": "Insulin with Prescription",
+ "product_tax_code": "51183603A0000"
+ },
+ {
+ "description": "Devices used by diabetic individuals to monitor sugar levels in the blood, sold under prescription order of a licensed professional.",
+ "name": "Blood Glucose Monitoring Devices with Prescription",
+ "product_tax_code": "41116201A0000"
+ },
+ {
+ "description": "Devices used by diabetic individuals to monitor sugar levels in the blood, sold without prescription order of a licensed professional.",
+ "name": "Blood Glucose Monitoring Devices without Prescription",
+ "product_tax_code": "41116201A0001"
+ },
+ {
+ "description": "At home urine-based tests used to detect the presense of various drug substances in an individual.",
+ "name": "Drug Testing Kits",
+ "product_tax_code": "41116136A0001"
+ },
+ {
+ "description": "Single use hollow needle commonly used with a syringe to inject insulin into the body by diabetic individuals, sold under prescription order of a licensed professional.",
+ "name": "Hypodermic Needles/Syringes with prescription - Insulin",
+ "product_tax_code": "42142523A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicare, medical grade oyxgen.",
+ "name": "Medical Oxygen with Prescription reimbursed by Medicare",
+ "product_tax_code": "42271700A0009"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicare, medical grade oyxgen.",
+ "name": "Medical Oxygen with Prescription billed to Medicare",
+ "product_tax_code": "42271700A0007"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, a replacement, corrective, or supportive device, worn on or in the body to: Artificially replace a missing portion of the body; Prevent or correct physical deformity or malfunction; or Support a weak or deformed portion of the body. Worn in or on the body means that the item is implanted or attached so that it becomes part of the body, or is carried by the body and does not hinder the mobility of the individual. Examples include artificial limbs, pacemakers, orthopedics, ostomy/colostomy devices, etc.",
+ "name": "Prosthetic Devices without Prescription",
+ "product_tax_code": "42242000A0006"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicaid, a replacement, corrective, or supportive device, worn on or in the body to: Artificially replace a missing portion of the body; Prevent or correct physical deformity or malfunction; or Support a weak or deformed portion of the body. Worn in or on the body means that the item is implanted or attached so that it becomes part of the body, or is carried by the body and does not hinder the mobility of the individual. Examples include artificial limbs, pacemakers, orthotics, orthopedics, ostomy/colostomy devices, catheters, etc.",
+ "name": "Prosthetic Devices with Prescription Billed to Medicaid",
+ "product_tax_code": "42242000A0003"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicare, a replacement, corrective, or supportive device, worn on or in the body to: Artificially replace a missing portion of the body; Prevent or correct physical deformity or malfunction; or Support a weak or deformed portion of the body. Worn in or on the body means that the item is implanted or attached so that it becomes part of the body, or is carried by the body and does not hinder the mobility of the individual. Examples include artificial limbs, pacemakers, orthotics, orthopedics, ostomy/colostomy devices, catheters, etc.",
+ "name": "Prosthetic Devices with Prescription Billed to Medicare",
+ "product_tax_code": "42242000A0002"
+ },
+ {
+ "description": "At home saliva, cheeek swab or blood drop based tests used to detect various genetic markers in an individual.",
+ "name": "DNA Testing Kits",
+ "product_tax_code": "41116205A0003"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicaid, nutritional tube feeding equipment including button-style feeding tubes, standard G-tubes, NG-tubes, extension sets, adapters, feeding pumps, feeding pump delivery sets.",
+ "name": "Enteral Feeding Equipment for home use with Prescription reimbursed by Medicaid",
+ "product_tax_code": "42231500A0005"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicaid, equipment used to administer oxygen directly into the lungs of the patient for the relief of conditions in which the human body experiences an abnormal deficiency or inadequate supply of oxygen. Oxygen equipment means oxygen cylinders, cylinder transport devices, including sheaths and carts, cylinder studs and support devices, regulators, flowmeters, tank wrenches, oxygen concentrators, liquid oxygen base dispensers, liquid oxygen portable dispensers, oxygen tubing, nasal cannulas, face masks, oxygen humidifiers, and oxygen fittings and accessories.",
+ "name": "Oxygen Delivery Equipment for home use with Prescription reimbursed by Medicaid",
+ "product_tax_code": "42271700A0005"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, equipment used to administer oxygen directly into the lungs of the patient for the relief of conditions in which the human body experiences an abnormal deficiency or inadequate supply of oxygen. Oxygen equipment means oxygen cylinders, cylinder transport devices, including sheaths and carts, cylinder studs and support devices, regulators, flowmeters, tank wrenches, oxygen concentrators, liquid oxygen base dispensers, liquid oxygen portable dispensers, oxygen tubing, nasal cannulas, face masks, oxygen humidifiers, and oxygen fittings and accessories.",
+ "name": "Oxygen Delivery Equipment for home use with Prescription",
+ "product_tax_code": "42271700A0001"
+ },
+ {
+ "description": "Synthetic or animal-based insulin used as an injectible drug for diabetes patients, sold without prescription order of a licensed professional.",
+ "name": "Insulin without Prescription",
+ "product_tax_code": "51183603A0001"
+ },
+ {
+ "description": "Single use hollow needle commonly used with a syringe to inject insulin into the body by diabetic individuals, sold without prescription order of a licensed professional.",
+ "name": "Hypodermic Needles/Syringes without prescription - Insulin",
+ "product_tax_code": "42142523A0001"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, equipment used to administer oxygen directly into the lungs of the patient for the relief of conditions in which the human body experiences an abnormal deficiency or inadequate supply of oxygen. Oxygen equipment means oxygen cylinders, cylinder transport devices, including sheaths and carts, cylinder studs and support devices, regulators, flowmeters, tank wrenches, oxygen concentrators, liquid oxygen base dispensers, liquid oxygen portable dispensers, oxygen tubing, nasal cannulas, face masks, oxygen humidifiers, and oxygen fittings and accessories.",
+ "name": "Oxygen Delivery Equipment for home use without Prescription",
+ "product_tax_code": "42271700A0006"
+ },
+ {
+ "description": "At home blood-prick based tests used to monitor cholesterol levels in an individual.",
+ "name": "Cholesterol Testing Kits",
+ "product_tax_code": "41116202A0001"
+ },
+ {
+ "description": "Single use supplies utilized by diabetics in the regular blood sugar monitoring regimen. Includes skin puncture lancets, test strips for blood glucose monitors, visual read test strips, and urine test strips, sold under prescription order of a licensed professional.",
+ "name": "Diabetic Testing Supplies - single use - with Prescription",
+ "product_tax_code": "41116120A0002"
+ },
+ {
+ "description": "A breast pump is a mechanical device that lactating women use to extract milk from their breasts. They may be manual devices powered by hand or foot movements or automatic devices powered by electricity.",
+ "name": "Breast Pumps",
+ "product_tax_code": "42231901A0000"
+ },
+ {
+ "description": "At home urine-based tests used to detect pregancy hormone levels.",
+ "name": "Pregenacy Testing Kits",
+ "product_tax_code": "41116205A0001"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, and reimbursed by Medicaid, equipment which is primarily and customarily used to provide or increase the ability to move from one place to another and which is appropriate for use either in a home or a motor vehicle; Is not generally used by persons with normal mobility; and Does not include any motor vehicle or equipment on a motor vehicle normally provided by a motor vehicle manufacturer. Examples include wheelchairs, crutches, canes, walkers, chair lifts, etc.",
+ "name": "Mobility Enhancing Equipment with Prescription Reimbursed by Medicaid",
+ "product_tax_code": "42211500A0005"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, a replacement, corrective, or supportive device, worn in the mouth, including dentures, orthodontics, crowns, bridges, etc.",
+ "name": "Dental Prosthetics without Prescription",
+ "product_tax_code": "42151500A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, a replacement, corrective, or supportive device, worn in the mouth, including dentures, orthodontics, crowns, bridges, etc.",
+ "name": "Dental Prosthetics with Prescription",
+ "product_tax_code": "42151500A0001"
+ },
+ {
+ "description": "At home urine-based tests used to detect impending ovulation to assist in pregnancy planning.",
+ "name": "Ovulation Testing Kits",
+ "product_tax_code": "41116205A0002"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, nutritional tube feeding equipment including button-style feeding tubes, standard G-tubes, NG-tubes, extension sets, adapters, feeding pumps, feeding pump delivery sets.",
+ "name": "Enteral Feeding Equipment for home use without Prescription",
+ "product_tax_code": "42231500A0006"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicare, nutritional tube feeding equipment including button-style feeding tubes, standard G-tubes, NG-tubes, extension sets, adapters, feeding pumps, feeding pump delivery sets.",
+ "name": "Enteral Feeding Equipment for home use with Prescription reimbursed by Medicare",
+ "product_tax_code": "42231500A0004"
+ },
+ {
+ "description": "When sold without prescription order of a licensed professional, equipment that: can withstand repeated use; is primarily and customarily used to serve a medical purpose; generally is not useful to a person in the absence of illness or injury; and is not worn in or on the body. Home use means the equipment is sold to an individual for use at home, regardless of where the individual resides. Examples include hospital beds, commode chairs, bed pans, IV poles, etc.",
+ "name": "Durable Medical Equipment for home use without Prescription",
+ "product_tax_code": "42140000A0006"
+ },
+ {
+ "description": "Male or female condoms used to prevent pregnancy or exposure to STDs, containing a spermicidal lubricant as indicated by a \"drug facts\" panel or a statement of active ingredients.",
+ "name": "Condoms with Spermicide",
+ "product_tax_code": "53131622A0001"
+ },
+ {
+ "description": "Single use supplies utilized by diabetics in the regular blood sugar monitoring regimen. Includes skin puncture lancets, test strips for blood glucose monitors, visual read test strips, and urine test strips.",
+ "name": "Diabetic Testing Supplies - single use - without Prescription",
+ "product_tax_code": "41116120A0001"
+ },
+ {
+ "description": "An electronic device that clips onto a patient's finger to measure heart rate and oxygen saturation in his or her red blood cells.",
+ "name": "Pulse Oximeter",
+ "product_tax_code": "42181801A0000"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, equipment which is primarily and customarily used to provide or increase the ability to move from one place to another and which is appropriate for use either in a home or a motor vehicle; Is not generally used by persons with normal mobility; and Does not include any motor vehicle or equipment on a motor vehicle normally provided by a motor vehicle manufacturer. Examples include wheelchairs, crutches, canes, walkers, chair lifts, etc.",
+ "name": "Mobility Enhancing Equipment with Prescription",
+ "product_tax_code": "42211500A0001"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, a replacement, corrective, or supportive device, worn on or in the body to: Artificially replace a missing portion of the body; Prevent or correct physical deformity or malfunction; or Support a weak or deformed portion of the body. Worn in or on the body means that the item is implanted or attached so that it becomes part of the body, or is carried by the body and does not hinder the mobility of the individual. Examples include artificial limbs, pacemakers, orthotics, orthopedics, ostomy/colostomy devices, catheters, etc.",
+ "name": "Prosthetic Devices with Prescription",
+ "product_tax_code": "42242000A0001"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicare, nutritional tube feeding equipment including button-style feeding tubes, standard G-tubes, NG-tubes, extension sets, adapters, feeding pumps, feeding pump delivery sets.",
+ "name": "Enteral Feeding Equipment for home use with Prescription billed to Medicare",
+ "product_tax_code": "42231500A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicaid, equipment that: can withstand repeated use; is primarily and customarily used to serve a medical purpose; generally is not useful to a person in the absence of illness or injury; and is not worn in or on the body. Home use means the equipment is sold to an individual for use at home, regardless of where the individual resides. Examples include hospital beds, commode chairs, bed pans, shower and bath aids, IV poles, etc.",
+ "name": "Durable Medical Equipment for home use with Prescription billed to Medicaid",
+ "product_tax_code": "42140000A0003"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicare, a replacement, corrective, or supportive device, worn on or in the body to: Artificially replace a missing portion of the body; Prevent or correct physical deformity or malfunction; or Support a weak or deformed portion of the body. Worn in or on the body means that the item is implanted or attached so that it becomes part of the body, or is carried by the body and does not hinder the mobility of the individual. Examples include artificial limbs, pacemakers, orthotics, orthopedics, ostomy/colostomy devices, catheters, etc.",
+ "name": "Prosthetic Devices with Prescription Reimbursed by Medicare",
+ "product_tax_code": "42242000A0004"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, and billed to Medicare, equipment which is primarily and customarily used to provide or increase the ability to move from one place to another and which is appropriate for use either in a home or a motor vehicle; Is not generally used by persons with normal mobility; and Does not include any motor vehicle or equipment on a motor vehicle normally provided by a motor vehicle manufacturer. Examples include wheelchairs, crutches, canes, walkers, chair lifts, etc.",
+ "name": "Mobility Enhancing Equipment with Prescription Billed to Medicare",
+ "product_tax_code": "42211500A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, and billed to Medicaid, equipment which is primarily and customarily used to provide or increase the ability to move from one place to another and which is appropriate for use either in a home or a motor vehicle; Is not generally used by persons with normal mobility; and Does not include any motor vehicle or equipment on a motor vehicle normally provided by a motor vehicle manufacturer. Examples include wheelchairs, crutches, canes, walkers, chair lifts, etc.",
+ "name": "Mobility Enhancing Equipment with Prescription Billed to Medicaid",
+ "product_tax_code": "42211500A0003"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicare, a machine used that filters a patient's blood to remove excess water and waste products when the kidneys are damaged, dysfunctional, or missing. The kidney dialysis machine is an artificial part which augments the natural functioning of the kidneys.",
+ "name": "Kidney Dialysis Equipment for home use with Prescription reimbursed by Medicare",
+ "product_tax_code": "42161800A0004"
+ },
+ {
+ "description": "At home digital or manual (aneroid) sphygmomanometers, also known as a blood pressure meter, blood pressure monitor, or blood pressure gauge, are devices used to measure blood pressure, composed of an inflatable cuff to collapse and then release the artery under the cuff in a controlled manner.",
+ "name": "Blood Pressure Testing Devices",
+ "product_tax_code": "42181600A0001"
+ },
+ {
+ "description": "A topical preparation containing a spermicidal lubricant to prevent pregnancy as indicated by a \"drug facts\" panel or a statement of active ingredients.",
+ "name": "Contraceptive Ointments",
+ "product_tax_code": "53131622A0002"
+ },
+ {
+ "description": "Male or female condoms used to prevent pregnacy or exposure to STDs.",
+ "name": "Condoms",
+ "product_tax_code": "53131622A0000"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicare, equipment that: can withstand repeated use; is primarily and customarily used to serve a medical purpose; generally is not useful to a person in the absence of illness or injury; and is not worn in or on the body. Home use means the equipment is sold to an individual for use at home, regardless of where the individual resides. Examples include hospital beds, commode chairs, bed pans, shower and bath aids, IV poles, etc.",
+ "name": "Durable Medical Equipment for home use with Prescription billed to Medicare",
+ "product_tax_code": "42140000A0002"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicaid, a replacement, corrective, or supportive device, worn on or in the body to: Artificially replace a missing portion of the body; Prevent or correct physical deformity or malfunction; or Support a weak or deformed portion of the body. Worn in or on the body means that the item is implanted or attached so that it becomes part of the body, or is carried by the body and does not hinder the mobility of the individual. Examples include artificial limbs, pacemakers, orthotics, orthopedics, ostomy/colostomy devices, catheters, etc.",
+ "name": "Prosthetic Devices with Prescription Reimbursed by Medicaid",
+ "product_tax_code": "42242000A0005"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, and reimbursed by Medicare, equipment which is primarily and customarily used to provide or increase the ability to move from one place to another and which is appropriate for use either in a home or a motor vehicle; Is not generally used by persons with normal mobility; and Does not include any motor vehicle or equipment on a motor vehicle normally provided by a motor vehicle manufacturer. Examples include wheelchairs, crutches, canes, walkers, chair lifts, etc.",
+ "name": "Mobility Enhancing Equipment with Prescription Reimbursed by Medicare",
+ "product_tax_code": "42211500A0004"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicaid, nutritional tube feeding equipment including button-style feeding tubes, standard G-tubes, NG-tubes, extension sets, adapters, feeding pumps, feeding pump delivery sets.",
+ "name": "Enteral Feeding Equipment for home use with prescription billed to Medicaid",
+ "product_tax_code": "42231500A0003"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, nutritional tube feeding equipment including button-style feeding tubes, standard G-tubes, NG-tubes, extension sets, adapters, feeding pumps, feeding pump delivery sets.",
+ "name": "Enteral Feeding Equipment for home use with Prescription",
+ "product_tax_code": "42231500A0001"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and billed directly to Medicaid, a machine used that filters a patient's blood to remove excess water and waste products when the kidneys are damaged, dysfunctional, or missing. The kidney dialysis machine is an artificial part which augments the natural functioning of the kidneys.",
+ "name": "Kidney Dialysis Equipment for home use with Prescription billed to Medicaid",
+ "product_tax_code": "42161800A0003"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional and reimbursed by Medicaid, a machine used that filters a patient's blood to remove excess water and waste products when the kidneys are damaged, dysfunctional, or missing. The kidney dialysis machine is an artificial part which augments the natural functioning of the kidneys.",
+ "name": "Kidney Dialysis Equipment for home use with Prescription and reimbursed by Medicaid",
+ "product_tax_code": "42161800A0005"
+ },
+ {
+ "description": "When sold under prescription order of a licensed professional, a machine used that filters a patient's blood to remove excess water and waste products when the kidneys are damaged, dysfunctional, or missing. The kidney dialysis machine is an artificial part which augments the natural functioning of the kidneys.",
+ "name": "Kidney Dialysis Equipment for home use with Prescription",
+ "product_tax_code": "42161800A0001"
+ },
+ {
+ "description": "Handbags, Purses",
+ "name": "Handbags, Purses",
+ "product_tax_code": "53121600A0000"
+ },
+ {
+ "description": "Video Gaming Console - Fixed",
+ "name": "Video Gaming Console - Fixed",
+ "product_tax_code": "52161557A0000"
+ },
+ {
+ "description": "Video Cameras",
+ "name": "Video Cameras",
+ "product_tax_code": "45121515A0000"
+ },
+ {
+ "description": "Portable audio equipment that records digital music for playback",
+ "name": "Digital Music Players",
+ "product_tax_code": "52161543A0000"
+ },
+ {
+ "description": "A type of consumer electronic device used to play vinyl recordings.",
+ "name": "Audio Turntables",
+ "product_tax_code": "52161548A0000"
+ },
+ {
+ "description": "Video Gaming Console - Portable",
+ "name": "Video Gaming Console - Portable",
+ "product_tax_code": "52161558A0000"
+ },
+ {
+ "description": "A framed display designed to display preloaded digital images (jpeg or any digital image format). Has slots for flash memory cards and/or an interface for digital photo camera connection.",
+ "name": "Digital Picture Frames",
+ "product_tax_code": "52161549A0000"
+ },
+ {
+ "description": "Digital Cameras",
+ "name": "Digital Cameras",
+ "product_tax_code": "45121504A0000"
+ },
+ {
+ "description": "Mobile Phones",
+ "name": "Mobile Phones",
+ "product_tax_code": "43191501A0000"
+ },
+ {
+ "description": "A digital wristwatch that provides many other features besides timekeeping. Like a smartphone, a smartwatch has a touchscreen display, which allows you to perform actions by tapping or swiping on the screen. Smartwatches include allow access to apps, similar to apps for smartphones and tablets.",
+ "name": "Watches - Smart",
+ "product_tax_code": "54111500A0001"
+ },
+ {
+ "description": "A bicycle helmet that is NOT marketed and labeled as being intended for youth.",
+ "name": "Bicycle Helmets - Adult",
+ "product_tax_code": "46181704A0003"
+ },
+ {
+ "description": "A bicycle helmet marketed and labeled as being intended for youth.",
+ "name": "Bicycle Helmets - Youth",
+ "product_tax_code": "46181704A0002"
+ },
+ {
+ "description": "Luggage",
+ "name": "Luggage",
+ "product_tax_code": "53121500A0000"
+ },
+ {
+ "description": "Clothing - Sleep or eye mask",
+ "name": "Clothing - Sleep or eye mask",
+ "product_tax_code": "53102607A0000"
+ },
+ {
+ "description": "Clothing - Pocket protectors",
+ "name": "Clothing - Pocket protectors",
+ "product_tax_code": "53102514A0000"
+ },
+ {
+ "description": "Clothing - Button covers",
+ "name": "Clothing - Button covers",
+ "product_tax_code": "53102515A0000"
+ },
+ {
+ "description": "Shoe Inserts/Insoles",
+ "name": "Clothing - Shoe Inserts/Insoles",
+ "product_tax_code": "46182208A0000"
+ },
+ {
+ "description": "Aprons - Household/Kitchen",
+ "name": "Clothing - Aprons - Household/Kitchen",
+ "product_tax_code": "46181501A0002"
+ },
+ {
+ "description": "Hunting Vests",
+ "name": "Clothing - Hunting Vests",
+ "product_tax_code": "53103100A0003"
+ },
+ {
+ "description": "Clothing apparel/uniforms that are specific to the training and competition of various martial arts.",
+ "name": "Clothing - Martial Arts Attire",
+ "product_tax_code": "53102717A0001"
+ },
+ {
+ "description": "Clothing - Umbrellas",
+ "name": "Clothing - Umbrellas",
+ "product_tax_code": "53102505A0000"
+ },
+ {
+ "description": "Briefcases",
+ "name": "Briefcases",
+ "product_tax_code": "53121701A0000"
+ },
+ {
+ "description": "Wallets",
+ "name": "Wallets",
+ "product_tax_code": "53121600A0001"
+ },
+ {
+ "description": "Wristwatch timepieces",
+ "name": "Watches",
+ "product_tax_code": "54111500A0000"
+ },
+ {
+ "description": "Jewelry",
+ "name": "Jewelry",
+ "product_tax_code": "54100000A0000"
+ },
+ {
+ "description": "Non-prescription sunglasses",
+ "name": "Sunglasses - Non-Rx",
+ "product_tax_code": "42142905A0001"
+ },
+ {
+ "description": "Wigs, Hairpieces, Hair extensions",
+ "name": "Clothing - Wigs, Hairpieces, Hair extensions",
+ "product_tax_code": "53102500A0002"
+ },
+ {
+ "description": "Hair notions, hair clips, barrettes, hair bows, hair nets, etc.",
+ "name": "Clothing - Hair Accessories",
+ "product_tax_code": "53102500A0001"
+ },
+ {
+ "description": "Clothing - Headbands",
+ "name": "Clothing - Headbands",
+ "product_tax_code": "53102513A0000"
+ },
+ {
+ "description": "These supplies contain medication such as an antibiotic ointment. They are a labeled with a \"drug facts\" panel or a statement of active ingredients. A wound care supply is defined as an item that is applied directly to or inside a wound to absorb wound drainage, protect healing tissue, maintain a moist or dry wound environment (as appropriate), or prevent bacterial contamination. Examples include bandages, dressings, gauze, medical tape. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Wound Care Supplies - Bandages, Dressings, Gauze - Medicated",
+ "product_tax_code": "42311514A0000"
+ },
+ {
+ "description": "A wound care supply is defined as an item that is applied directly to or inside a wound to absorb wound drainage, protect healing tissue, maintain a moist or dry wound environment (as appropriate), or prevent bacterial contamination. Examples include bandages, dressings, gauze, medical tape. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Wound Care Supplies - Bandages, Dressings, Gauze",
+ "product_tax_code": "42311500A0001"
+ },
+ {
+ "description": "Toothpaste containing \"drug facts\" panel or a statement of active ingredients. These products do contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Toothpaste",
+ "product_tax_code": "53131502A0000"
+ },
+ {
+ "description": "Disposable moistened cleansing wipes - non medicated. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Baby Wipes/Cleansing Wipes",
+ "product_tax_code": "53131624A0000"
+ },
+ {
+ "description": "Toilet Paper. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Toilet Paper",
+ "product_tax_code": "14111704A0000"
+ },
+ {
+ "description": "A lotion, spray, gel, foam, stick or other topical product that absorbs or reflects some of the sun's ultraviolet (UV) radiation and thus helps protect against sunburn. Sunscreen contains a \"drug facts\" label or statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Sunscreen",
+ "product_tax_code": "53131609A0000"
+ },
+ {
+ "description": "Soaps, body washes, shower gels for personal hygiene containing antibacterial. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Soaps - Antibacterial",
+ "product_tax_code": "53131608A0001"
+ },
+ {
+ "description": "Over-the-counter nicotine replacement products, including patches, gum, lozenges, sprays and inhalers. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Smoking Cessation Products",
+ "product_tax_code": "51143218A0000"
+ },
+ {
+ "description": "Lotions, moisturizers, creams, powders, sprays, etc that promote optimal skin health. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Skin Care Products- Medicated",
+ "product_tax_code": "51241200A0001"
+ },
+ {
+ "description": "A hair care product for cleansing the hair/scalp, with anti-dandruff active ingredients. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Shampoo - medicated (anti-dandruff)",
+ "product_tax_code": "53131628A0001"
+ },
+ {
+ "description": "A multi-purpose skin protectorant and topical ointment. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Petroleum Jelly",
+ "product_tax_code": "53131641A0000"
+ },
+ {
+ "description": "An over-the-counter drug via RX is a substance that contains a label identifying it as a drug and including a \"drug facts\" panel or a statement of active ingredients, that can be obtained without a prescription, but is sold under prescription order of a licensed professional. A drug can be intended for internal (ingestible, implant, injectable) or external (topical) application to the human body. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Over-the-Counter Drugs via Prescription",
+ "product_tax_code": "51030"
+ },
+ {
+ "description": "Flexible adhesive strips that attach over the bridge of the nose to lift the sides of the nose, opening the nasal passages to provide relief for congestion and snoring. The products are drug free and contain no active drug ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Nasal Breathing Strips",
+ "product_tax_code": "42312402A0001"
+ },
+ {
+ "description": "Therapeutic mouthwash, having active ingredients (such as antiseptic, or flouride) intended to help control or reduce conditions like bad breath, gingivitis, plaque, and tooth decay. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Mouthwash - Therapeutic",
+ "product_tax_code": "53131501A0000"
+ },
+ {
+ "description": "Multiple use medical thermometers for oral, temporal/forehead, or rectal body temperature diagnostics. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Medical Thermometers - Reusable",
+ "product_tax_code": "42182200A0002"
+ },
+ {
+ "description": "One-time use medical thermometers for oral, temporal/forehead, or rectal body temperature diagnostics. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Medical Thermometers - Disposable",
+ "product_tax_code": "42182200A0001"
+ },
+ {
+ "description": "Masks designed for one-time use to protect the wearer from contamination of breathable particles. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Medical Masks",
+ "product_tax_code": "42131713A0001"
+ },
+ {
+ "description": "A medicated skin protectorant for the lips. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Lip Balm - Medicated",
+ "product_tax_code": "53131630A0001"
+ },
+ {
+ "description": "A skin protectorant for the lips. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Lip Balm",
+ "product_tax_code": "53131630A0000"
+ },
+ {
+ "description": "Artificial devices to correct or alleviate hearing deficiencies, sold under prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hearing Aids with Prescription",
+ "product_tax_code": "42211705A0000"
+ },
+ {
+ "description": "Artificial deives to correct or alleviate hearing deficiencies, sold without a prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hearing Aids without Prescription",
+ "product_tax_code": "42211705A0001"
+ },
+ {
+ "description": "Batteries specifically labeled and designed to operate hearing aid devices, sold under prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hearing Aid Batteries with Prescription",
+ "product_tax_code": "26111710A0001"
+ },
+ {
+ "description": "Batteries specifically labeled and designed to operate hearing aid devices, sold without a prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hearing Aid Batteries without Prescription",
+ "product_tax_code": "26111710A0002"
+ },
+ {
+ "description": "A liquid, gel, foam, or wipe generally used to decrease infectious agents on the hands. Alcohol-based versions typically contain some combination of isopropyl alcohol, ethanol (ethyl alcohol), or n-propanol. Alcohol-free products are generally based on disinfectants, or on antimicrobial agents. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hand Sanitizers",
+ "product_tax_code": "53131626A0000"
+ },
+ {
+ "description": "Topical foams, creams, gels, etc that prevent hair loss and promote hair regrowth. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hair Loss Products",
+ "product_tax_code": "51182001A0001"
+ },
+ {
+ "description": "A collection of mixed supplies and equipment that is used to give medical treatment, often housed in durable plastic boxes, fabric pouches or in wall mounted cabinets. Exempt or low rated qualifying medicinal items (eg. OTC drugs) make up 51% or more of the value of the kit. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "First Aid Kits - 51% or more medicinal items",
+ "product_tax_code": "42172001A0002"
+ },
+ {
+ "description": "A collection of mixed supplies and equipment that is used to give medical treatment, often housed in durable plastic boxes, fabric pouches or in wall mounted cabinets. Exempt or low rated qualifying medicinal items (eg. OTC drugs) make up 50% or less of the value of the kit. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "First Aid Kits - 50% or less medicinal items",
+ "product_tax_code": "42172001A0001"
+ },
+ {
+ "description": "Over-the-counter antifungal creams, ointments or suppositories to treat yeast infections, containing a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Feminine Yeast Treatments",
+ "product_tax_code": "51302300A0001"
+ },
+ {
+ "description": "Vaginal cleaning products include douches and wipes with medication such as an antiseptic, containing a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Feminine Cleansing Solutions - Medicated",
+ "product_tax_code": "53131615A0002"
+ },
+ {
+ "description": "Vaginal cleaning products include douches and wipes. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Feminine Cleansing Solutions",
+ "product_tax_code": "53131615A0001"
+ },
+ {
+ "description": "Single use disposable gloves (latex, nitrile, vinyl, etc) that while appropriate for multiple uses, have an application in a first aid or medical setting. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Disposable Gloves",
+ "product_tax_code": "42132203A0000"
+ },
+ {
+ "description": "Personal under-arm deodorants/antiperspirants. These products do contain a \"drug facts\" panel or a statement of active ingredients, typically aluminum. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Deodorant/Antiperspirant",
+ "product_tax_code": "53131606A0000"
+ },
+ {
+ "description": "Denture adhesives are pastes, powders or adhesive pads that may be placed in/on dentures to help them stay in place. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Denture creams/adhesives",
+ "product_tax_code": "53131510A0000"
+ },
+ {
+ "description": "Toothbrushes. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Toothbrushes",
+ "product_tax_code": "53131503A0000"
+ },
+ {
+ "description": "Dental Floss/picks. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Dental Floss/picks",
+ "product_tax_code": "53131504A0000"
+ },
+ {
+ "description": "Single use cotton balls or swabs for multi-purpose use other than applying medicines and cleaning wounds, due to not being sterile. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Cotton Balls/Swabs - Unsterile",
+ "product_tax_code": "42141500A0002"
+ },
+ {
+ "description": "Single use cotton balls or swabs for application of antiseptics and medications and to cleanse scratches, cuts or minor wounds. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Cotton Balls/Swabs - Sterile",
+ "product_tax_code": "42141500A0001"
+ },
+ {
+ "description": "Corrective lenses, eyeglasses, sold under prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Corrective Lenses, Eyeglasses with Prescription",
+ "product_tax_code": "42142900A0001"
+ },
+ {
+ "description": "Corrective lenses, including eyeglasses and contact lenses, sold without a prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Corrective Lenses without Prescription",
+ "product_tax_code": "42142900A0002"
+ },
+ {
+ "description": "Liquid solution for lubricating/rewetting, but not disinfecting, contact lenses. This solution is applied directly to the lens, rather then inserted into the eye. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Contact Lens Lubricating Solutions - For lens",
+ "product_tax_code": "42142914A0001"
+ },
+ {
+ "description": "Liquid solution for lubricating/rewetting, but not disinfecting, contact lenses. This solution is applied directly to the eye. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Contact Lens Lubricating Solutions - For eyes",
+ "product_tax_code": "42142914A0002"
+ },
+ {
+ "description": "Contact lenses, sold under prescription order of a licensed professional. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Contact Lenses with Prescription",
+ "product_tax_code": "42142913A0000"
+ },
+ {
+ "description": "Liquid solution for cleaning and disinfecting contact lenses. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Contact Lens Disinfecting Solutions",
+ "product_tax_code": "42142914A0000"
+ },
+ {
+ "description": "A reusable pain management supply that includes artificial ice packs, gel packs, heat wraps, etc used for pain relief. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Cold or Hot Therapy Packs - Reusable",
+ "product_tax_code": "42142100A0002"
+ },
+ {
+ "description": "A heating pad is a pad used for warming of parts of the body in order to manage pain. Types of heating pads include electrical, chemical and hot water bottles. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Heating Pads",
+ "product_tax_code": "42142100A0001"
+ },
+ {
+ "description": "A single use pain management supply that includes artificial ice packs, gel packs, heat wraps, etc used for pain relief. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Cold or Hot Therapy Packs - Disposable - Medicated",
+ "product_tax_code": "42142100A0004"
+ },
+ {
+ "description": "A single use pain management supply that includes artificial ice packs, gel packs, heat wraps, etc used for pain relief. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Cold or Hot Therapy Packs - Disposable",
+ "product_tax_code": "42142100A0003"
+ },
+ {
+ "description": "Baby powder is an astringent powder used for preventing diaper rash, as a spray, and for other cosmetic uses. It may be composed of talcum (in which case it is also called talcum powder) or corn starch. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Baby Powder",
+ "product_tax_code": "53131649A0001"
+ },
+ {
+ "description": "Baby oil is an inert (typically mineral) oil for the purpose of keeping skin soft and supple. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Baby Oil",
+ "product_tax_code": "51241900A0001"
+ },
+ {
+ "description": "A cosmetic foam or gel used for shaving preparation. The purpose of shaving cream is to soften the hair by providing lubrication. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Shaving Creams",
+ "product_tax_code": "53131611A0000"
+ },
+ {
+ "description": "Personal under-arm deodorants/antiperspirants containing natural ingredients and/or ingredients that are not considered drugs. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Deodorant - Natural or no active ingredients",
+ "product_tax_code": "53131606A0001"
+ },
+ {
+ "description": "A hair care product for cleansing the hair/scalp. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Shampoo",
+ "product_tax_code": "53131628A0000"
+ },
+ {
+ "description": "Various surfactant preparations to improve cleaning, enhance the enjoyment of bathing, and serve as a vehicle for cosmetic agents. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Bubble Bath, Bath Salts/Oils/Crystals",
+ "product_tax_code": "53131612A0001"
+ },
+ {
+ "description": "Cosmetic mouthwash may temporarily control bad breath and leave behind a pleasant taste, but has no chemical or biological application beyond their temporary benefit. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Mouthwash - Cosmetic",
+ "product_tax_code": "53131501A0001"
+ },
+ {
+ "description": "Teeth whitening gels, rinse, strips, trays, etc containing bleaching agents. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Teeth Whitening Kits",
+ "product_tax_code": "42151506A0000"
+ },
+ {
+ "description": "A hair care product typically applied and rinsed after shampooing that is used to improve the feel, appearance and manageability of hair. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Conditioner - Hair",
+ "product_tax_code": "53131628A0002"
+ },
+ {
+ "description": "Depilatories are cosmetic preparations used to remove hair from the skin. Chemical depilatories are available in gel, cream, lotion, aerosol, roll-on, and powder forms. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hair Removal Products",
+ "product_tax_code": "53131623A0000"
+ },
+ {
+ "description": "Breath spray is a product sprayed into the mouth and breath strips dissolve in the mouth for the purpose of eliminating halitosis. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Breath Spray/dissolvable strips",
+ "product_tax_code": "53131509A0000"
+ },
+ {
+ "description": "Lotions, moisturizers, creams, powders, sprays, etc that promote optimal skin health. These products do not contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Skin Care Products",
+ "product_tax_code": "51241200A0002"
+ },
+ {
+ "description": "Liquid drops to be placed inside the ear canal to reduce the symptoms of an ear ache, or to act as an ear drying aid, or to loosen, cleanse, and aid in the removal of ear wax. These products contain a \"drug facts\" panel or a statement of active ingredients. Examples include Ear Ache, Swimmers' Ears, and Ear Wax removal drops. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Ear Drops - Medicated",
+ "product_tax_code": "51241000A0001"
+ },
+ {
+ "description": "Topical medicated solutions for treating skin acne. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Acne Treatments",
+ "product_tax_code": "51241400A0001"
+ },
+ {
+ "description": "A skin cream forming a protective barrier to help heal and soothe diaper rash discomfort. These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Diaper Cream",
+ "product_tax_code": "51241859A0001"
+ },
+ {
+ "description": "A liquid solution typically used as a topical antiseptic. The products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Isopropyl (Rubbing) Alcohol",
+ "product_tax_code": "51471901A0000"
+ },
+ {
+ "description": "Hydrogen peroxide is a mild antiseptic used on the skin to prevent infection of minor cuts, scrapes, and burns. It may also be used as a mouth rinse to help remove mucus or to relieve minor mouth irritation (e.g., due to canker/cold sores, gingivitis). These products contain a \"drug facts\" panel or a statement of active ingredients. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Hydrogen Peroxide",
+ "product_tax_code": "51473503A0000"
+ },
+ {
+ "description": "Articles intended to be rubbed, poured, sprinkled, or sprayed on, introduced into, or otherwise applied to the human body or any part thereof for beautifying, promoting attractiveness, or altering the appearance. This category supports only the following items: Acrylic fingernail glue, Acrylic fingernails, Artificial eyelashes, Blush, Bronzer, Body glitter, Concealer, Eyelash glue, Finger/toenail decorations, Finger/toenail polish, Nail polish remover, Hair coloring, Hair mousse/gel, Hair oil, Hair spray, Hair relaxer, Hair wave treatment, Hair wax, Lip gloss, Lip liner, Lipstick, Liquid foundation, Makeup, Mascara, Nail polish remover, Powder foundation, Cologne, Perfume. This code is intended for sales directly to end consumers that are NOT healthcare providers.",
+ "name": "Cosmetics - Beautifying",
+ "product_tax_code": "53131619A0001"
+ },
+ {
+ "description": "Power cords",
+ "name": "Power cords",
+ "product_tax_code": "26121636A0000"
+ },
+ {
+ "description": "Archery accessories including quivers, releases, arrow shafts, armguards, hunting belts, bow parts, cleaning products, mounted safety equipment, scopes, sights, hunting slings, string wax, targets, target throwers, etc.",
+ "name": "Archery Accessories",
+ "product_tax_code": "49181602A0002"
+ },
+ {
+ "description": "Landscape soil, mulch, compost - residential",
+ "name": "Landscape Soil, Mulch, Compost - Residential",
+ "product_tax_code": "11121700A0001"
+ },
+ {
+ "description": "Firearms, limited to pistols, revolvers, rifles with a barrel no greater than an internal diameter of .50 caliber or a shotguns of 10 gauge or smaller.",
+ "name": "Firearms",
+ "product_tax_code": "46101500A0001"
+ },
+ {
+ "description": "Firearm accessories including repair parts, cleaning products, holsters, mounted safety equipment, choke tubes, scopes, shooting tripod/bipod/monopod, shooting bags/pouches, sights, etc.",
+ "name": "Firearm Accessories",
+ "product_tax_code": "46101506A0001"
+ },
+ {
+ "description": "Portable fuel container",
+ "name": "Portable Fuel Container",
+ "product_tax_code": "24111808A0001"
+ },
+ {
+ "description": "Hard and soft cases designed specifically for firearms equipment",
+ "name": "Gun Cases",
+ "product_tax_code": "46101801A0000"
+ },
+ {
+ "description": "Primary archery equipment including bows, crossbow, and bow strings.",
+ "name": "Archery Equipment",
+ "product_tax_code": "49181602A0001"
+ },
+ {
+ "description": "Hard and soft cases designed specifically for archery equipment.",
+ "name": "Archery Cases",
+ "product_tax_code": "46101801A0001"
+ },
+ {
+ "description": "Protective earmuffs to muffle the sound of gunfire.",
+ "name": "Hearing Protection Earmuffs",
+ "product_tax_code": "46181902A0001"
+ },
+ {
+ "description": "Ammunition for firearms with a barrel no greater than an internal diameter of .50 caliber or a shotgun of 10 gauge or smaller., including bullets, shotgun shells, and gunpowder.",
+ "name": "Ammunition",
+ "product_tax_code": "46101600A0001"
+ },
+ {
+ "description": "Bedclothes items including sheets, pillow cases, bedspreads, comforters, blankets, throws, duvet covers, pillow shams, bed skirts, mattress pad, mattress toppers, and pillows.",
+ "name": "Bedding",
+ "product_tax_code": "52121500A0000"
+ },
+ {
+ "description": "Towels used for individual drying of persons, including bath towels, beach towels, wash cloths, hand towels, fact towels, sport towels, etc.",
+ "name": "Bath towels",
+ "product_tax_code": "52121700A0000"
+ },
+ {
+ "description": "WaterSense labeled urinals.",
+ "name": "Urinals - WaterSense",
+ "product_tax_code": "30181506A0000"
+ },
+ {
+ "description": "WaterSense labeled toilets.",
+ "name": "Toilets - WaterSense",
+ "product_tax_code": "30181505A0000"
+ },
+ {
+ "description": "WaterSense labeled irrigation controllers, which act like a thermostat for your sprinkler system telling it when to turn on and off, use local weather and landscape conditions to tailor watering schedules to actual conditions on the site.",
+ "name": "Irrigation Controls - WaterSense",
+ "product_tax_code": "21102503A0001"
+ },
+ {
+ "description": "Ceiling Fans carrying an Energy Star rating.",
+ "name": "Ceiling fans - Energy Star",
+ "product_tax_code": "40101609A0000"
+ },
+ {
+ "description": "Standard incandescent light bulbs carrying an Energy Star rating.",
+ "name": "Incandescent Light Bulbs - Energy Star",
+ "product_tax_code": "39101612A0001"
+ },
+ {
+ "description": "Compact Fluorescent light (CFL) bulbs carrying an Energy Star rating.",
+ "name": "Compact Fluorescent Light Bulbs - Energy Star",
+ "product_tax_code": "39101619A0001"
+ },
+ {
+ "description": "Domestic appliance carrying an Energy Star Rating which reduces and maintains the level of humidity in the air.",
+ "name": "Dehumidifier - Energy Star",
+ "product_tax_code": "40101902A0000"
+ },
+ {
+ "description": "Domestic air conditioning (central or room) systems carrying Energy Star rating.",
+ "name": "Air conditioners - Energy Star",
+ "product_tax_code": "40101701A0000"
+ },
+ {
+ "description": "Artificial ice, blue ice, ice packs, reusable ice",
+ "name": "Artificial Ice",
+ "product_tax_code": "24121512A0000"
+ },
+ {
+ "description": "A port replicator is an attachment for a notebook computer that allows a number of devices such as a printer, large monitor, and keyboard to be simultaneously connected.",
+ "name": "Port Replicators",
+ "product_tax_code": "43211603A0000"
+ },
+ {
+ "description": "Computer Mouse/Pointing Devices",
+ "name": "Computer Mouse/Pointing Devices",
+ "product_tax_code": "43211708A0000"
+ },
+ {
+ "description": "Storage drives, hard drives, Zip drives, etc.",
+ "name": "Computer Drives",
+ "product_tax_code": "43201800A0001"
+ },
+ {
+ "description": "An in home programmable thermostat, such as a WiFi enabled smart thermostat, carrying an Energy Star rating.",
+ "name": "Programmable Wall Thermostat - Energy Star",
+ "product_tax_code": "41112209A0001"
+ },
+ {
+ "description": "Domestic gas or oil boilers for space or water heating carrying an Energy Star rating.",
+ "name": "Boilers - Energy Star",
+ "product_tax_code": "40102004A0001"
+ },
+ {
+ "description": "Domestic water heater carrying Energy Star rating.",
+ "name": "Water heater - Energy Star",
+ "product_tax_code": "40101825A0000"
+ },
+ {
+ "description": "Domestic freezers carrying Energy Star rating.",
+ "name": "Freezers- Energy Star",
+ "product_tax_code": "52141506A0000"
+ },
+ {
+ "description": "Domestic air source heat pumps carrying Energy Star rating.",
+ "name": "Heat Pumps - Energy Star",
+ "product_tax_code": "40101806A0000"
+ },
+ {
+ "description": "Domestic gas or oil furnaces carrying an Energy Star rating.",
+ "name": "Furnaces - Energy Star",
+ "product_tax_code": "40101805A0000"
+ },
+ {
+ "description": "Plywood, window film, storm shutters, hurricane shutters or other materials specifically designed to protect windows.",
+ "name": "Storm shutters/window protection devices",
+ "product_tax_code": "30151801A0001"
+ },
+ {
+ "description": "Smoke Detectors",
+ "name": "Smoke Detectors",
+ "product_tax_code": "46191501A0000"
+ },
+ {
+ "description": "Mobile phone charging device/cord",
+ "name": "Mobile Phone Charging Device/cord",
+ "product_tax_code": "43191501A0002"
+ },
+ {
+ "description": "A webcam is a video camera that feeds or streams an image or video in real time to or through a computer to a computer network, such as the Internet. Webcams are typically small cameras that sit on a desk, attach to a user's monitor, or are built into the hardware",
+ "name": "Web Camera",
+ "product_tax_code": "45121520A0000"
+ },
+ {
+ "description": "A sound card is an expansion component used in computers to receive and send audio.",
+ "name": "Sound Cards",
+ "product_tax_code": "43201502A0000"
+ },
+ {
+ "description": "Computer Speakers",
+ "name": "Computer Speakers",
+ "product_tax_code": "43211607A0000"
+ },
+ {
+ "description": "Computer Microphones",
+ "name": "Computer Microphones",
+ "product_tax_code": "43211719A0000"
+ },
+ {
+ "description": "A docking station is a hardware frame and set of electrical connection interfaces that enable a notebook computer to effectively serve as a desktop computer.",
+ "name": "Docking Stations",
+ "product_tax_code": "43211602A0000"
+ },
+ {
+ "description": "Computer Batteries",
+ "name": "Computer Batteries",
+ "product_tax_code": "26111711A0001"
+ },
+ {
+ "description": "Computer Monitor/Displays",
+ "name": "Computer Monitor/Displays",
+ "product_tax_code": "43211900A0000"
+ },
+ {
+ "description": "Computer Keyboards",
+ "name": "Computer Keyboards",
+ "product_tax_code": "43211706A0000"
+ },
+ {
+ "description": "Printer Ink",
+ "name": "Printer Ink",
+ "product_tax_code": "44103105A0000"
+ },
+ {
+ "description": "Printer Paper",
+ "name": "Printer Paper",
+ "product_tax_code": "14111507A0000"
+ },
+ {
+ "description": "Non-electric can opener",
+ "name": "Can opener - manual",
+ "product_tax_code": "52151605A0001"
+ },
+ {
+ "description": "Sheet music - Student",
+ "name": "Sheet music - Student",
+ "product_tax_code": "55101514A0000"
+ },
+ {
+ "description": "Musical instruments - Student",
+ "name": "Musical instruments - Student",
+ "product_tax_code": "60130000A0001"
+ },
+ {
+ "description": "Reference printed material commonly used by a student in a course of study as a reference and to learn the subject being taught.",
+ "name": "Dictionaries/Thesauruses",
+ "product_tax_code": "55101526A0001"
+ },
+ {
+ "description": "An item commonly used by a student in a course of study for artwork. This category is limited to the following items...clay and glazes, paints, paintbrushes for artwork, sketch and drawing pads, watercolors.",
+ "name": "School Art Supplies",
+ "product_tax_code": "60121200A0001"
+ },
+ {
+ "description": "A calendar based notebook to aid in outlining one's daily appointments, classes, activities, etc.",
+ "name": "Daily Planners",
+ "product_tax_code": "44112004A0001"
+ },
+ {
+ "description": "Portable self-powered or battery powered radio, two-way radio, weatherband radio.",
+ "name": "Portable Radios",
+ "product_tax_code": "43191510A0000"
+ },
+ {
+ "description": "Single or multi-pack AA, AAA, c, D, 6-volt or 9-volt batteries, excluding automobile or boat batteries.",
+ "name": "Alkaline Batteries",
+ "product_tax_code": "26111702A0000"
+ },
+ {
+ "description": "Routers",
+ "name": "Routers",
+ "product_tax_code": "43222609A0000"
+ },
+ {
+ "description": "Removable storage media such as compact disks, flash drives, thumb drives, flash memory cards.",
+ "name": "Computer Storage Media",
+ "product_tax_code": "43202000A0000"
+ },
+ {
+ "description": "Computer Printer",
+ "name": "Computer Printer",
+ "product_tax_code": "43212100A0001"
+ },
+ {
+ "description": "Portable self-powered or battery powered light sources, including flashlights, lanterns, emergency glow sticks or light sticks.",
+ "name": "Portable Light Sources",
+ "product_tax_code": "39111610A0000"
+ },
+ {
+ "description": "Canned software on tangible media that is used for non-recreational purposes, such as Antivirus, Database, Educational, Financial, Word processing, etc.",
+ "name": "Software - Prewritten, tangible media - Non-recreational",
+ "product_tax_code": "43230000A1101"
+ },
+ {
+ "description": "Personal computers, including laptops, tablets, desktops.",
+ "name": "Personal Computers",
+ "product_tax_code": "43211500A0001"
+ },
+ {
+ "description": "A device that joins pages of paper or similar material by fastening a thin metal staple through the sheets and folding the ends underneath.",
+ "name": "Staplers/Staples",
+ "product_tax_code": "44121615A0000"
+ },
+ {
+ "description": "Pins/tacks to secure papers, pictures, calendars, etc. to bulletin boards, walls, etc.",
+ "name": "Push pins/tacks",
+ "product_tax_code": "44122106A0000"
+ },
+ {
+ "description": "Bags/packs designed to carry students' books during the school day. This category does not include backpags for traveling, hiking, camping, etc.",
+ "name": "Bookbags/Backpacks - Student",
+ "product_tax_code": "53121603A0001"
+ },
+ {
+ "description": "Ground anchor systems and tie down kits for securing property against severe weather.",
+ "name": "Ground Anchor Systems and Tie-down Kits",
+ "product_tax_code": "31162108A0000"
+ },
+ {
+ "description": "An expansion card that allows the computer to send graphical information to a video display device such as a monitor, TV, or projector. Video cards are often used by gamers in place of integrated graphics due to their extra processing power and video ram.",
+ "name": "Video/Graphics Card",
+ "product_tax_code": "43201401A0000"
+ },
+ {
+ "description": "Scanners",
+ "name": "Scanners",
+ "product_tax_code": "43211711A0000"
+ },
+ {
+ "description": "Modems",
+ "name": "Modems",
+ "product_tax_code": "43222628A0000"
+ },
+ {
+ "description": "A map that could be used by a student in a course of study as a reference and to learn the subject being taught.",
+ "name": "Maps - Student",
+ "product_tax_code": "60103410A0001"
+ },
+ {
+ "description": "Tarps, plastic sheeting, plastic drop cloths, waterproof sheeting.",
+ "name": "Tarpaulins and Weatherproof Sheeting",
+ "product_tax_code": "24141506A0000"
+ },
+ {
+ "description": "Portable generator used to provide light or communications or power appliances during a power outage.",
+ "name": "Portable Generator",
+ "product_tax_code": "26111604A0001"
+ },
+ {
+ "description": "Headphones/Earbuds",
+ "name": "Headphones/Earbuds",
+ "product_tax_code": "52161514A0000"
+ },
+ {
+ "description": "A portable electronic device for reading digital books and periodicals.",
+ "name": "E-Book Readers",
+ "product_tax_code": "43211519A0000"
+ },
+ {
+ "description": "Mobile phone batteries",
+ "name": "Mobile Phone Batteries",
+ "product_tax_code": "43191501A0001"
+ },
+ {
+ "description": "A globe that could be used by a student in a course of study as a reference and to learn the subject being taught.",
+ "name": "Globes - Student",
+ "product_tax_code": "60104414A0001"
+ },
+ {
+ "description": "Domestic standard size refrigerators carrying Energy Star rating.",
+ "name": "Refrigerators - Energy Star",
+ "product_tax_code": "52141501A0000"
+ },
+ {
+ "description": "Non-electric food or beverage cooler.",
+ "name": "Food Storage Cooler",
+ "product_tax_code": "52152002A0001"
+ },
+ {
+ "description": "A motherboard is the physical component in a computer that contains the computer's basic circuitry and other components",
+ "name": "Motherboards",
+ "product_tax_code": "43201513A0000"
+ },
+ {
+ "description": "Calculators",
+ "name": "Calculators",
+ "product_tax_code": "44101807A0000"
+ },
+ {
+ "description": "Axes/Hatchets",
+ "name": "Axes/Hatchets",
+ "product_tax_code": "27112005A0000"
+ },
+ {
+ "description": "Water conserving products are for conserving or retaining groundwater; recharging water tables; or decreasing ambient air temperature, and so limiting water evaporation. Examples include soil sufactants, a soaker or drip-irrigation hose, a moisture control for a sprinkler or irrigation system, a rain barrel or an alternative rain and moisture collection system, a permeable ground cover surface that allows water to reach underground basins, aquifers or water collection points.",
+ "name": "Water Conserving Products",
+ "product_tax_code": "21102500A0001"
+ },
+ {
+ "description": "Domestic clothes drying appliances carrying Energy Star rating.",
+ "name": "Clothes drying machine - Energy Star",
+ "product_tax_code": "52141602A0000"
+ },
+ {
+ "description": "Software - Prewritten, electronic delivery - Business Use",
+ "name": "Software - Prewritten, electronic delivery - Business Use",
+ "product_tax_code": "43230000A9200"
+ },
+ {
+ "description": "Software - Custom, electronic delivery - Business Use",
+ "name": "Software - Custom, electronic delivery - Business Use",
+ "product_tax_code": "43230000A9201"
+ },
+ {
+ "description": "Food and Beverage - Sugar and Sugar Substitutes",
+ "name": "Food and Beverage - Sugar and Sugar Substitutes",
+ "product_tax_code": "50161900A0000"
+ },
+ {
+ "description": "Food and Beverage - Eggs and egg products",
+ "name": "Food and Beverage - Eggs and egg products",
+ "product_tax_code": "50131600A0000"
+ },
+ {
+ "description": "Food and Beverage - Coffee, coffee substitutes and tea",
+ "name": "Food and Beverage - Coffee, coffee substitutes and tea",
+ "product_tax_code": "50201700A0000"
+ },
+ {
+ "description": "Food and Beverage - Candy",
+ "name": "Food and Beverage - Candy",
+ "product_tax_code": "50161800A0000"
+ },
+ {
+ "description": "Food and Beverage - Butter, Margarine, Shortening and Cooking Oils",
+ "name": "Food and Beverage - Butter, Margarine, Shortening and Cooking Oils",
+ "product_tax_code": "50151500A0000"
+ },
+ {
+ "description": "Clothing - Facial shields",
+ "name": "Clothing - Facial shields",
+ "product_tax_code": "46181702A0001"
+ },
+ {
+ "description": "Clothing - Hard hats",
+ "name": "Clothing - Hard hats",
+ "product_tax_code": "46181701A0001"
+ },
+ {
+ "description": "Clothing - Cleanroom footwear",
+ "name": "Clothing - Cleanroom footwear",
+ "product_tax_code": "46181603A0001"
+ },
+ {
+ "description": "Clothing - Fire retardant footwear",
+ "name": "Clothing - Fire retardant footwear",
+ "product_tax_code": "46181601A0001"
+ },
+ {
+ "description": "Clothing - Protective pants",
+ "name": "Clothing - Protective pants",
+ "product_tax_code": "46181527A0001"
+ },
+ {
+ "description": "Clothing - Garters",
+ "name": "Clothing - Garters",
+ "product_tax_code": "53102509A0000"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered electronically (includes support services only - no updates/upgrades)",
+ "name": "Software maintenance and support - Optional, custom, electronic delivery (support services only)",
+ "product_tax_code": "81112200A2222"
+ },
+ {
+ "description": "Software maintenance and support - Mandatory maintenance and support charges for prewritten software including items delivered by load and leave",
+ "name": "Software maintenance and support - Mandatory, prewritten, load and leave delivery",
+ "product_tax_code": "81112200A1310"
+ },
+ {
+ "description": "Software maintenance and support - Mandatory maintenance and support charges for prewritten software including items delivered electronically",
+ "name": "Software maintenance and support - Mandatory, prewritten, electronic delivery",
+ "product_tax_code": "81112200A1210"
+ },
+ {
+ "description": "Electronic software documentation or user manuals - For prewritten software & delivered electronically",
+ "name": "Electronic software documentation or user manuals - Prewritten, electronic delivery",
+ "product_tax_code": "55111601A1200"
+ },
+ {
+ "description": "Clothing - Fur Ear muffs or scarves",
+ "name": "Clothing - Fur Ear muffs or scarves",
+ "product_tax_code": "53102502A0001"
+ },
+ {
+ "description": "Clothing - Safety glasses",
+ "name": "Clothing - Safety glasses",
+ "product_tax_code": "46181802A0001"
+ },
+ {
+ "description": "Software - Prewritten & delivered on tangible media",
+ "name": "Software - Prewritten, tangible media",
+ "product_tax_code": "43230000A1100"
+ },
+ {
+ "description": "Mainframe administration services\r\n",
+ "name": "Mainframe administration services\r\n",
+ "product_tax_code": "81111802A0000"
+ },
+ {
+ "description": "Co-location service",
+ "name": "Co-location service",
+ "product_tax_code": "81111814A0000"
+ },
+ {
+ "description": "Information management system for mine action IMSMA\r\n",
+ "name": "Information management system for mine action IMSMA\r\n",
+ "product_tax_code": "81111710A0000"
+ },
+ {
+ "description": "Local area network LAN maintenance or support",
+ "name": "Local area network LAN maintenance or support",
+ "product_tax_code": "81111803A0000"
+ },
+ {
+ "description": "Data center services",
+ "name": "Data center services",
+ "product_tax_code": "81112003A0000"
+ },
+ {
+ "description": "Wide area network WAN maintenance or support",
+ "name": "Wide area network WAN maintenance or support",
+ "product_tax_code": "81111804A0000"
+ },
+ {
+ "description": "Electronic data interchange EDI design\r\n",
+ "name": "Electronic data interchange EDI design\r\n",
+ "product_tax_code": "81111703A0000"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for prewritten software including items delivered on tangible media (includes support services only - no updates/upgrades)",
+ "name": "Software maintenance and support - Optional, prewritten, tangible media (support services only)",
+ "product_tax_code": "81112200A1122"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for prewritten software including items delivered on tangible media (includes software updates/upgrades)",
+ "name": "Software maintenance and support - Optional, prewritten, tangible media (incl. updates/upgrades)",
+ "product_tax_code": "81112200A1121"
+ },
+ {
+ "description": "Clothing - Safety sleeves",
+ "name": "Clothing - Safety sleeves",
+ "product_tax_code": "46181516A0001"
+ },
+ {
+ "description": "Clothing - Fire retardant apparel",
+ "name": "Clothing - Fire retardant apparel",
+ "product_tax_code": "46181508A0001"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for prewritten software including items delivered by load and leave (includes support services only - no updates/upgrades)",
+ "name": "Software maintenance and support - Optional, prewritten, load and leave delivery (support services only)",
+ "product_tax_code": "81112200A1322"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for prewritten software including items delivered by load and leave (includes software updates/upgrades)",
+ "name": "Software maintenance and support - Optional, prewritten, load and leave delivery (incl. updates/upgrades)",
+ "product_tax_code": "81112200A1321"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for prewritten software including items delivered electronically (includes support services only - no updates/upgrades)",
+ "name": "Software maintenance and support - Optional, prewritten, electronic delivery (support services only)",
+ "product_tax_code": "81112200A1222"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered on tangible media (includes support services only - no updates/upgrades)",
+ "name": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered on tangible media (includes support services only - no updates/upgrades)\r\n",
+ "product_tax_code": "81112200A2122"
+ },
+ {
+ "description": "Clothing - Protective ponchos",
+ "name": "Clothing - Protective ponchos",
+ "product_tax_code": "46181506A0001"
+ },
+ {
+ "description": "Clothing - Protective gloves",
+ "name": "Clothing - Protective gloves",
+ "product_tax_code": "46181504A0001"
+ },
+ {
+ "description": "Clothing - Synthetic Fur Vest",
+ "name": "Clothing - Synthetic Fur Vest",
+ "product_tax_code": "53103100A0002"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered on tangible media (includes software updates/upgrades)",
+ "name": "Software maintenance and support - Optional, custom, tangible media (incl. updates/upgrades)",
+ "product_tax_code": "81112200A2121"
+ },
+ {
+ "description": "Computer graphics service\r\n",
+ "name": "Computer graphics service\r\n",
+ "product_tax_code": "81111512A0000"
+ },
+ {
+ "description": "Proprietary or licensed systems maintenance or support",
+ "name": "Proprietary or licensed systems maintenance or support",
+ "product_tax_code": "81111805A0000"
+ },
+ {
+ "description": "Software - Prewritten & delivered electronically",
+ "name": "Software - Prewritten, electronic delivery",
+ "product_tax_code": "43230000A1200"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for prewritten software including items delivered electronically (includes software updates/upgrades)",
+ "name": "Software maintenance and support - Optional, prewritten, electronic delivery (incl. updates/upgrades)",
+ "product_tax_code": "81112200A1221"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered by load and leave (includes support services only - no updates/upgrades)",
+ "name": "Software maintenance and support - Optional, custom, load and leave delivery (support services only)",
+ "product_tax_code": "81112200A2322"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered by load and leave (includes software updates/upgrades)",
+ "name": "Software maintenance and support - Optional, custom, load and leave delivery (incl. updates/upgrades)",
+ "product_tax_code": "81112200A2321"
+ },
+ {
+ "description": "Software maintenance and support - Optional maintenance and support charges for custom software including items delivered electronically (includes software updates/upgrades)",
+ "name": "Software maintenance and support - Optional, custom, electronic delivery (incl. updates/upgrades)",
+ "product_tax_code": "81112200A2221"
+ },
+ {
+ "description": "Software maintenance and support - Mandatory maintenance and support charges for custom software including items delivered on tangible media",
+ "name": "Software maintenance and support - Mandatory, custom, tangible media",
+ "product_tax_code": "81112200A2110"
+ },
+ {
+ "description": "Software maintenance and support - Mandatory maintenance and support charges for custom software including items delivered electronically",
+ "name": "Software maintenance and support - Mandatory, custom, electronic delivery",
+ "product_tax_code": "81112200A2210"
+ },
+ {
+ "description": "Food and Beverage - Fish and seafood",
+ "name": "Food and Beverage - Fish and seafood",
+ "product_tax_code": "50121500A0000"
+ },
+ {
+ "description": "Food and Beverage - Ice cubes",
+ "name": "Food and Beverage - Ice cubes",
+ "product_tax_code": "50202302A0000"
+ },
+ {
+ "description": "Food and Beverage - Cooking Ingredients",
+ "name": "Food and Beverage - Cooking Ingredients",
+ "product_tax_code": "50181700A0000"
+ },
+ {
+ "description": "Food and Beverage - Cocoa and Cocoa products",
+ "name": "Food and Beverage - Cocoa and Cocoa products",
+ "product_tax_code": "50161511A0000"
+ },
+ {
+ "description": "Food and Beverage - Baby foods and formulas",
+ "name": "Food and Beverage - Baby foods and formulas",
+ "product_tax_code": "42231800A0000"
+ },
+ {
+ "description": "Clothing - Hazardous material protective footwear",
+ "name": "Clothing - Hazardous material protective footwear",
+ "product_tax_code": "46181602A0001"
+ },
+ {
+ "description": "Clothing - Welder gloves",
+ "name": "Clothing - Welder gloves",
+ "product_tax_code": "46181540A0001"
+ },
+ {
+ "description": "Clothing - Protective shirts",
+ "name": "Clothing - Protective shirts",
+ "product_tax_code": "46181526A0001"
+ },
+ {
+ "description": "Gift Cards",
+ "name": "Gift Cards",
+ "product_tax_code": "14111803A0001"
+ },
+ {
+ "description": "Clothing - Leg protectors",
+ "name": "Clothing - Leg protectors",
+ "product_tax_code": "46181520A0001"
+ },
+ {
+ "description": "Clothing - Protective coveralls",
+ "name": "Clothing - Protective coveralls",
+ "product_tax_code": "46181503A0001"
+ },
+ {
+ "description": "Internet or intranet client application development services\r\n",
+ "name": "Internet or intranet client application development services\r\n",
+ "product_tax_code": "81111509A0000"
+ },
+ {
+ "description": "Database design\r\n",
+ "name": "Database design\r\n",
+ "product_tax_code": "81111704A0000"
+ },
+ {
+ "description": "Computer programmers\r\n",
+ "name": "Computer programmers\r\n",
+ "product_tax_code": "81111600A0000"
+ },
+ {
+ "description": "Clothing - Synthetic Fur Hat",
+ "name": "Clothing - Synthetic Fur Hat",
+ "product_tax_code": "53102504A0002"
+ },
+ {
+ "description": "System or application programming management service\r\n",
+ "name": "System or application programming management service\r\n",
+ "product_tax_code": "81111511A0000"
+ },
+ {
+ "description": "Food and Beverage - Fruit",
+ "name": "Food and Beverage - Fruit",
+ "product_tax_code": "50300000A0000"
+ },
+ {
+ "description": "Food and Beverage - Vegetables",
+ "name": "Food and Beverage - Vegetables",
+ "product_tax_code": "50400000A0000"
+ },
+ {
+ "description": "Food and Beverage - Dried fruit, unsweetened",
+ "name": "Food and Beverage - Dried fruit, unsweetened",
+ "product_tax_code": "50320000A0000"
+ },
+ {
+ "description": "Food and Beverage - Snack Foods",
+ "name": "Food and Beverage - Snack Foods",
+ "product_tax_code": "50192100A0000"
+ },
+ {
+ "description": "Food and Beverage - Processed Nuts and Seeds",
+ "name": "Food and Beverage - Nuts and seeds that have been processed or treated by salting, spicing, smoking, roasting, or other means",
+ "product_tax_code": "50101716A0001"
+ },
+ {
+ "description": "Food and Beverage - Non-Alcoholic Beer, Wine",
+ "name": "Food and Beverage - Non-Alcoholic Beer, Wine",
+ "product_tax_code": "50202300A0001"
+ },
+ {
+ "description": "Food and Beverage - Ice Cream, sold in container less than one pint",
+ "name": "Food and Beverage - Ice Cream, sold in container less than one pint",
+ "product_tax_code": "50192304A0000"
+ },
+ {
+ "description": "Food and Beverage - Alcoholic beverages - Spirits",
+ "name": "Food and Beverage - Alcoholic beverages - Spirits",
+ "product_tax_code": "50202206A0000"
+ },
+ {
+ "description": "Food and Beverage - Wine",
+ "name": "Food and Beverage - Alcoholic beverages - Wine",
+ "product_tax_code": "50202203A0000"
+ },
+ {
+ "description": "Electronic content bundle - Delivered electronically with less than permanent rights of usage and streamed",
+ "name": "Electronic content bundle - Delivered electronically with less than permanent rights of usage and streamed",
+ "product_tax_code": "55111500A9220"
+ },
+ {
+ "description": "Clothing - Welding masks",
+ "name": "Clothing - Welding masks",
+ "product_tax_code": "46181703A0001"
+ },
+ {
+ "description": "Clothing - Protective wear dispenser",
+ "name": "Clothing - Protective wear dispenser",
+ "product_tax_code": "46181553A0001"
+ },
+ {
+ "description": "Clothing - Anti cut gloves",
+ "name": "Clothing - Anti cut gloves",
+ "product_tax_code": "46181536A0001"
+ },
+ {
+ "description": "Clothing - Reflective apparel or accessories",
+ "name": "Clothing - Reflective apparel or accessories",
+ "product_tax_code": "46181531A0001"
+ },
+ {
+ "description": "Clothing - Heat resistant clothing",
+ "name": "Clothing - Heat resistant clothing",
+ "product_tax_code": "46181518A0001"
+ },
+ {
+ "description": "Clothing - Cleanroom apparel",
+ "name": "Clothing - Cleanroom apparel",
+ "product_tax_code": "46181512A0001"
+ },
+ {
+ "description": "Clothing - Hazardous material protective apparel",
+ "name": "Clothing - Hazardous material protective apparel",
+ "product_tax_code": "46181509A0001"
+ },
+ {
+ "description": "Clothing - Safety vests",
+ "name": "Clothing - Safety vests",
+ "product_tax_code": "46181507A0001"
+ },
+ {
+ "description": "Clothing - Protective knee pads",
+ "name": "Clothing - Protective knee pads",
+ "product_tax_code": "46181505A0001"
+ },
+ {
+ "description": "Clothing - Bullet proof vests",
+ "name": "Clothing - Bullet proof vests",
+ "product_tax_code": "46181502A0001"
+ },
+ {
+ "description": "Clothing - Vest or waistcoats",
+ "name": "Clothing - Vest or waistcoats",
+ "product_tax_code": "53103100A0000"
+ },
+ {
+ "description": "Clothing - Prisoner uniform",
+ "name": "Clothing - Prisoner uniform",
+ "product_tax_code": "53102716A0000"
+ },
+ {
+ "description": "Clothing - Paramedic uniforms",
+ "name": "Clothing - Paramedic uniforms",
+ "product_tax_code": "53102712A0000"
+ },
+ {
+ "description": "Clothing - Ambulance officers uniforms",
+ "name": "Clothing - Ambulance officers uniforms",
+ "product_tax_code": "53102709A0000"
+ },
+ {
+ "description": "Clothing - Doctors coat",
+ "name": "Clothing - Doctors coat",
+ "product_tax_code": "53102707A0000"
+ },
+ {
+ "description": "Clothing - Sweat bands",
+ "name": "Clothing - Sweat bands",
+ "product_tax_code": "53102506A0000"
+ },
+ {
+ "description": "Clothing - Helmet parts or accessories",
+ "name": "Clothing - Helmet parts or accessories",
+ "product_tax_code": "46181706A0001"
+ },
+ {
+ "description": "Clothing - Fur Vest",
+ "name": "Clothing - Fur Vest",
+ "product_tax_code": "53103100A0001"
+ },
+ {
+ "description": "Clothing - Fur Gloves",
+ "name": "Clothing - Fur Gloves",
+ "product_tax_code": "53102503A0001"
+ },
+ {
+ "description": "Clothing - Motorcycle helmets",
+ "name": "Clothing - Motorcycle helmets",
+ "product_tax_code": "46181705A0001"
+ },
+ {
+ "description": "Operating system programming services\r\n",
+ "name": "Operating system programming services\r\n",
+ "product_tax_code": "81111505A0000"
+ },
+ {
+ "description": "Local area network communications design\r\n",
+ "name": "Local area network communications design\r\n",
+ "product_tax_code": "81111702A0000"
+ },
+ {
+ "description": "Clothing - Eye shields",
+ "name": "Clothing - Eye shields",
+ "product_tax_code": "46181803A0001"
+ },
+ {
+ "description": "Clothing - Welders helmet",
+ "name": "Clothing - Welders helmet",
+ "product_tax_code": "46181711A0001"
+ },
+ {
+ "description": "Clothing - Footwear covers",
+ "name": "Clothing - Footwear covers",
+ "product_tax_code": "46181606A0001"
+ },
+ {
+ "description": "Clothing - Cooling vest",
+ "name": "Clothing - Cooling vest",
+ "product_tax_code": "46181554A0001"
+ },
+ {
+ "description": "Clothing - Protective mesh jacket",
+ "name": "Clothing - Protective mesh jacket",
+ "product_tax_code": "46181551A0001"
+ },
+ {
+ "description": "Clothing - Protective scarf",
+ "name": "Clothing - Protective scarf",
+ "product_tax_code": "46181550A0001"
+ },
+ {
+ "description": "Clothing - Neck gaitor",
+ "name": "Clothing - Neck gaitor",
+ "product_tax_code": "46181549A0001"
+ },
+ {
+ "description": "Clothing - Welder bib",
+ "name": "Clothing - Welder bib",
+ "product_tax_code": "46181548A0001"
+ },
+ {
+ "description": "Clothing - Waterproof cap cover",
+ "name": "Clothing - Waterproof cap cover",
+ "product_tax_code": "46181547A0001"
+ },
+ {
+ "description": "Clothing - Waterproof suit",
+ "name": "Clothing - Waterproof suit",
+ "product_tax_code": "46181545A0001"
+ },
+ {
+ "description": "Clothing - Waterproof trousers or pants",
+ "name": "Clothing - Waterproof trousers or pants",
+ "product_tax_code": "46181544A0001"
+ },
+ {
+ "description": "Clothing - Protective mittens",
+ "name": "Clothing - Protective mittens",
+ "product_tax_code": "46181542A0001"
+ },
+ {
+ "description": "Clothing - Chemical resistant gloves",
+ "name": "Clothing - Chemical resistant gloves",
+ "product_tax_code": "46181541A0001"
+ },
+ {
+ "description": "Clothing - Anti vibratory gloves",
+ "name": "Clothing - Anti vibratory gloves",
+ "product_tax_code": "46181539A0001"
+ },
+ {
+ "description": "Clothing - Thermal gloves",
+ "name": "Clothing - Thermal gloves",
+ "product_tax_code": "46181538A0001"
+ },
+ {
+ "description": "Clothing - Insulated gloves",
+ "name": "Clothing - Insulated gloves",
+ "product_tax_code": "46181537A0001"
+ },
+ {
+ "description": "Clothing - Protective socks or hosiery",
+ "name": "Clothing - Protective socks or hosiery",
+ "product_tax_code": "46181535A0001"
+ },
+ {
+ "description": "Clothing - Protective wristbands",
+ "name": "Clothing - Protective wristbands",
+ "product_tax_code": "46181534A0001"
+ },
+ {
+ "description": "Clothing - Protective coats",
+ "name": "Clothing - Protective coats",
+ "product_tax_code": "46181533A0001"
+ },
+ {
+ "description": "Clothing - Insulated clothing for cold environments",
+ "name": "Clothing - Insulated clothing for cold environments",
+ "product_tax_code": "46181529A0001"
+ },
+ {
+ "description": "Clothing - Protective frock",
+ "name": "Clothing - Protective frock",
+ "product_tax_code": "46181528A0001"
+ },
+ {
+ "description": "Clothing - Safety hoods",
+ "name": "Clothing - Safety hoods",
+ "product_tax_code": "46181522A0001"
+ },
+ {
+ "description": "Clothing - Insulated or flotation suits",
+ "name": "Clothing - Insulated or flotation suits",
+ "product_tax_code": "46181517A0001"
+ },
+ {
+ "description": "Clothing - Elbow protectors",
+ "name": "Clothing - Elbow protectors",
+ "product_tax_code": "46181514A0001"
+ },
+ {
+ "description": "Clothing - Protective aprons",
+ "name": "Clothing - Protective aprons",
+ "product_tax_code": "46181501A0001"
+ },
+ {
+ "description": "Clothing - Shoes",
+ "name": "Clothing - Shoes",
+ "product_tax_code": "53111600A0000"
+ },
+ {
+ "description": "Clothing - Athletic wear",
+ "name": "Clothing - Athletic wear",
+ "product_tax_code": "53102900A0000"
+ },
+ {
+ "description": "Clothing - Folkloric clothing",
+ "name": "Clothing - Folkloric clothing",
+ "product_tax_code": "53102200A0000"
+ },
+ {
+ "description": "Clothing - Overalls or coveralls",
+ "name": "Clothing - Overalls or coveralls",
+ "product_tax_code": "53102100A0000"
+ },
+ {
+ "description": "Clothing - Dresses or skirts or saris or kimonos",
+ "name": "Clothing - Dresses or skirts or saris or kimonos",
+ "product_tax_code": "53102000A0000"
+ },
+ {
+ "description": "Clothing - Suits",
+ "name": "Clothing - Suits",
+ "product_tax_code": "53101900A0000"
+ },
+ {
+ "description": "Clothing - Sport uniform",
+ "name": "Clothing - Sport uniform",
+ "product_tax_code": "53102717A0000"
+ },
+ {
+ "description": "Clothing - Judicial robe",
+ "name": "Clothing - Judicial robe",
+ "product_tax_code": "53102714A0000"
+ },
+ {
+ "description": "Clothing - Ushers uniforms",
+ "name": "Clothing - Ushers uniforms",
+ "product_tax_code": "53102713A0000"
+ },
+ {
+ "description": "Clothing - Nurses uniforms",
+ "name": "Clothing - Nurses uniforms",
+ "product_tax_code": "53102708A0000"
+ },
+ {
+ "description": "Clothing - School uniforms",
+ "name": "Clothing - School uniforms",
+ "product_tax_code": "53102705A0000"
+ },
+ {
+ "description": "Clothing - Institutional food preparation or service attire",
+ "name": "Clothing - Institutional food preparation or service attire",
+ "product_tax_code": "53102704A0000"
+ },
+ {
+ "description": "Clothing - Police uniforms",
+ "name": "Clothing - Police uniforms",
+ "product_tax_code": "53102703A0000"
+ },
+ {
+ "description": "Clothing - Customs uniforms",
+ "name": "Clothing - Customs uniforms",
+ "product_tax_code": "53102702A0000"
+ },
+ {
+ "description": "Clothing - Bandannas",
+ "name": "Clothing - Bandannas",
+ "product_tax_code": "53102511A0000"
+ },
+ {
+ "description": "Clothing - Armbands",
+ "name": "Clothing - Armbands",
+ "product_tax_code": "53102508A0000"
+ },
+ {
+ "description": "Clothing - Caps",
+ "name": "Clothing - Caps",
+ "product_tax_code": "53102516A0000"
+ },
+ {
+ "description": "Clothing - Protective finger cots",
+ "name": "Clothing - Protective finger cots",
+ "product_tax_code": "46181530A0001"
+ },
+ {
+ "description": "Application programming services\r\n",
+ "name": "Application programming services\r\n",
+ "product_tax_code": "81111504A0000"
+ },
+ {
+ "description": "Application implementation services\r\n",
+ "name": "Application implementation services\r\n",
+ "product_tax_code": "81111508A0000"
+ },
+ {
+ "description": "Clothing - Synthetic Fur Ear muffs or scarves",
+ "name": "Clothing - Synthetic Fur Ear muffs or scarves",
+ "product_tax_code": "53102502A0002"
+ },
+ {
+ "description": "Clothing - Fur Poncho or Cape",
+ "name": "Clothing - Fur Poncho or Cape",
+ "product_tax_code": "53101806A0001"
+ },
+ {
+ "description": "Food and Beverage - Vitamins and Supplements - labeled with nutritional facts",
+ "name": "Food and Beverage - Vitamins and Supplements - labeled with nutritional facts",
+ "product_tax_code": "50501500A0001"
+ },
+ {
+ "description": "Food and Beverage - Nuts and seeds",
+ "name": "Food and Beverage - Nuts and seeds",
+ "product_tax_code": "50101716A0000"
+ },
+ {
+ "description": "Food and Beverage - Milk Substitutes",
+ "name": "Food and Beverage - Milk Substitutes",
+ "product_tax_code": "50151515A9000"
+ },
+ {
+ "description": "Food and Beverage - Milk and milk products",
+ "name": "Food and Beverage - Milk and milk products",
+ "product_tax_code": "50131700A0000"
+ },
+ {
+ "description": "Food and Beverage - Cheese",
+ "name": "Food and Beverage - Cheese",
+ "product_tax_code": "50131800A0000"
+ },
+ {
+ "description": "Clothing - Sandals",
+ "name": "Clothing - Sandals",
+ "product_tax_code": "53111800A0000"
+ },
+ {
+ "description": "Clothing - Pajamas or nightshirts or robes",
+ "name": "Clothing - Pajamas or nightshirts or robes",
+ "product_tax_code": "53102600A0000"
+ },
+ {
+ "description": "Clothing - Sweaters",
+ "name": "Clothing - Sweaters",
+ "product_tax_code": "53101700A0000"
+ },
+ {
+ "description": "Clothing - Slacks or trousers or shorts",
+ "name": "Clothing - Slacks or trousers or shorts",
+ "product_tax_code": "53101500A0000"
+ },
+ {
+ "description": "Clothing - Firefighter uniform",
+ "name": "Clothing - Firefighter uniform",
+ "product_tax_code": "53102718A0000"
+ },
+ {
+ "description": "Clothing - Salon smocks",
+ "name": "Clothing - Salon smocks",
+ "product_tax_code": "53102711A0000"
+ },
+ {
+ "description": "Clothing - Military uniforms",
+ "name": "Clothing - Military uniforms",
+ "product_tax_code": "53102701A0000"
+ },
+ {
+ "description": "Clothing - Heel pads",
+ "name": "Clothing - Heel pads",
+ "product_tax_code": "53112003A0000"
+ },
+ {
+ "description": "Clothing - Shoelaces",
+ "name": "Clothing - Shoelaces",
+ "product_tax_code": "53112002A0000"
+ },
+ {
+ "description": "Clothing - Infant swaddles or buntings or receiving blankets",
+ "name": "Clothing - Infant swaddles or buntings or receiving blankets",
+ "product_tax_code": "53102608A0000"
+ },
+ {
+ "description": "Clothing - Hats",
+ "name": "Clothing - Hats",
+ "product_tax_code": "53102503A0000"
+ },
+ {
+ "description": "Clothing - Ties or scarves or mufflers",
+ "name": "Clothing - Ties or scarves or mufflers",
+ "product_tax_code": "53102502A0000"
+ },
+ {
+ "description": "Clothing - Belts or suspenders",
+ "name": "Clothing - Belts or suspenders",
+ "product_tax_code": "53102501A0000"
+ },
+ {
+ "description": "Clothing - Tights",
+ "name": "Clothing - Tights",
+ "product_tax_code": "53102404A0000"
+ },
+ {
+ "description": "Clothing - Disposable youth training pants",
+ "name": "Clothing - Disposable youth training pants",
+ "product_tax_code": "53102311A0000"
+ },
+ {
+ "description": "Clothing - Undershirts",
+ "name": "Clothing - Undershirts",
+ "product_tax_code": "53102301A0000"
+ },
+ {
+ "description": "Clothing - Insulated cold weather shoe",
+ "name": "Clothing - Insulated cold weather shoe",
+ "product_tax_code": "46181610A0000"
+ },
+ {
+ "description": "Food and Beverage - Grains, Rice, Cereal",
+ "name": "Food and Beverage - Grains, Rice, Cereal",
+ "product_tax_code": "50221200A0000"
+ },
+ {
+ "description": "Clothing - Shirts",
+ "name": "Clothing - Shirts",
+ "product_tax_code": "53101600A0000"
+ },
+ {
+ "description": "Clothing - Safety boots",
+ "name": "Clothing - Safety boots",
+ "product_tax_code": "46181604A0000"
+ },
+ {
+ "description": "Clothing - Shin guards",
+ "name": "Clothing - Shin guards",
+ "product_tax_code": "49161525A0001"
+ },
+ {
+ "description": "Clothing - Athletic supporter",
+ "name": "Clothing - Athletic supporter",
+ "product_tax_code": "49161517A0001"
+ },
+ {
+ "description": "Clothing - Cleated or spiked shoes",
+ "name": "Clothing - Cleated or spiked shoes",
+ "product_tax_code": "53111900A0002"
+ },
+ {
+ "description": "Wide area network communications design\r\n",
+ "name": "Wide area network communications design\r\n\r\n",
+ "product_tax_code": "81111701A0000"
+ },
+ {
+ "description": "Systems integration design\r\n",
+ "name": "Systems integration design\r\n",
+ "product_tax_code": "81111503A0000"
+ },
+ {
+ "description": "Clothing - Bridal Gown",
+ "name": "Clothing - Bridal Gown",
+ "product_tax_code": "53101801A0004"
+ },
+ {
+ "description": "Clothing - Waterproof cap",
+ "name": "Clothing - Waterproof cap",
+ "product_tax_code": "46181546A0000"
+ },
+ {
+ "description": "Food and Beverage - Yogurt",
+ "name": "Food and Beverage - Yogurt",
+ "product_tax_code": "50131800A0001"
+ },
+ {
+ "description": "Food and Beverage - Nut Butters",
+ "name": "Food and Beverage - Nut Butters",
+ "product_tax_code": "50480000A9000"
+ },
+ {
+ "description": "Food and Beverage - Jams and Jellies",
+ "name": "Food and Beverage - Jams and Jellies",
+ "product_tax_code": "50192401A0000"
+ },
+ {
+ "description": "Food and Beverage - Honey, Maple Syrup",
+ "name": "Food and Beverage - Honey, Maple Syrup",
+ "product_tax_code": "50161509A0000"
+ },
+ {
+ "description": "Food and Beverage - Foods for Immediate Consumption",
+ "name": "Food and Beverage - Foods for Immediate Consumption",
+ "product_tax_code": "90100000A0001"
+ },
+ {
+ "description": "Food and Beverage - Bread and Flour Products",
+ "name": "Food and Beverage - Bread and Flour Products",
+ "product_tax_code": "50180000A0000"
+ },
+ {
+ "description": "Clothing - Overshoes",
+ "name": "Clothing - Overshoes",
+ "product_tax_code": "53112000A0000"
+ },
+ {
+ "description": "Clothing - Athletic footwear",
+ "name": "Clothing - Athletic footwear",
+ "product_tax_code": "53111900A0000"
+ },
+ {
+ "description": "Clothing - Slippers",
+ "name": "Clothing - Slippers",
+ "product_tax_code": "53111700A0000"
+ },
+ {
+ "description": "Clothing - Boots",
+ "name": "Clothing - Boots",
+ "product_tax_code": "53111500A0000"
+ },
+ {
+ "description": "Clothing - T-Shirts",
+ "name": "Clothing - T-Shirts",
+ "product_tax_code": "53103000A0000"
+ },
+ {
+ "description": "Clothing - Swimwear",
+ "name": "Clothing - Swimwear",
+ "product_tax_code": "53102800A0000"
+ },
+ {
+ "description": "Clothing - Coats or jackets",
+ "name": "Clothing - Coats or jackets",
+ "product_tax_code": "53101800A0000"
+ },
+ {
+ "description": "Clothing - Prison officer uniform",
+ "name": "Clothing - Prison officer uniform",
+ "product_tax_code": "53102715A0000"
+ },
+ {
+ "description": "Clothing - Corporate uniforms",
+ "name": "Clothing - Corporate uniforms",
+ "product_tax_code": "53102710A0000"
+ },
+ {
+ "description": "Clothing - Security uniforms",
+ "name": "Clothing - Security uniforms",
+ "product_tax_code": "53102706A0000"
+ },
+ {
+ "description": "Clothing - Chevrons",
+ "name": "Clothing - Chevrons",
+ "product_tax_code": "53102518A0000"
+ },
+ {
+ "description": "Clothing - Disposable work coat",
+ "name": "Clothing - Disposable work coat",
+ "product_tax_code": "53103201A0000"
+ },
+ {
+ "description": "Clothing - Bath robes",
+ "name": "Clothing - Bath robes",
+ "product_tax_code": "53102606A0000"
+ },
+ {
+ "description": "Clothing - Bib",
+ "name": "Clothing - Bib",
+ "product_tax_code": "53102521A0000"
+ },
+ {
+ "description": "Clothing - Gloves or mittens",
+ "name": "Clothing - Gloves or mittens",
+ "product_tax_code": "53102504A0000"
+ },
+ {
+ "description": "Clothing - Mouth guards",
+ "name": "Clothing - Mouth guards",
+ "product_tax_code": "42152402A0001"
+ },
+ {
+ "description": "Clothing - Boxing gloves",
+ "name": "Clothing - Boxing gloves",
+ "product_tax_code": "49171600A0000"
+ },
+ {
+ "description": "Clothing - Golf shoes",
+ "name": "Clothing - Golf shoes",
+ "product_tax_code": "53111900A0004"
+ },
+ {
+ "description": "Clothing - Bowling shoes",
+ "name": "Clothing - Bowling shoes",
+ "product_tax_code": "53111900A0003"
+ },
+ {
+ "description": "Internet or intranet server application development services\r\n",
+ "name": "Internet or intranet server application development services\r\n",
+ "product_tax_code": "81111510A0000"
+ },
+ {
+ "description": "Data conversion service\r\n",
+ "name": "Data conversion service\r\n",
+ "product_tax_code": "81112010A0000"
+ },
+ {
+ "description": "Client or server programming services\r\n",
+ "name": "Client or server programming services\r\n",
+ "product_tax_code": "81111506A0000"
+ },
+ {
+ "description": "Clothing - Ballet or tap shoes",
+ "name": "Clothing - Ballet or tap shoes",
+ "product_tax_code": "53111900A0001"
+ },
+ {
+ "description": "Clothing - Golf gloves",
+ "name": "Clothing - Golf gloves",
+ "product_tax_code": "49211606A0000"
+ },
+ {
+ "description": "Hardware as a service (HaaS)",
+ "name": "Hardware as a service (HaaS)",
+ "product_tax_code": "81161900A0000"
+ },
+ {
+ "description": "Cloud-based platform as a service (PaaS) - Personal Use",
+ "name": "Cloud-based platform as a service (PaaS) - Personal Use",
+ "product_tax_code": "81162100A0000"
+ },
+ {
+ "description": "Clothing - Panty hose",
+ "name": "Clothing - Panty hose",
+ "product_tax_code": "53102403A0000"
+ },
+ {
+ "description": "Clothing - Brassieres",
+ "name": "Clothing - Brassieres",
+ "product_tax_code": "53102304A0000"
+ },
+ {
+ "description": "Clothing - Protective sandals",
+ "name": "Clothing - Protective sandals",
+ "product_tax_code": "46181608A0000"
+ },
+ {
+ "description": "Clothing - Tuxedo or Formalwear",
+ "name": "Clothing - Tuxedo or Formalwear",
+ "product_tax_code": "53101801A0001"
+ },
+ {
+ "description": "Clothing - Lab coats",
+ "name": "Clothing - Lab coats",
+ "product_tax_code": "46181532A0000"
+ },
+ {
+ "description": "Systems planning services\r\n",
+ "name": "Systems planning services\r\n",
+ "product_tax_code": "81111707A0000"
+ },
+ {
+ "description": "Food and Beverage - Vitamins and Supplements",
+ "name": "Food and Beverage - Vitamins and Supplements - labeled with supplement facts",
+ "product_tax_code": "50501500A0000"
+ },
+ {
+ "description": "Food and Beverage - Jello and pudding mixes",
+ "name": "Food and Beverage - Jello and pudding mixes",
+ "product_tax_code": "50192404A0000"
+ },
+ {
+ "description": "Food and Beverage - Cooking spices",
+ "name": "Food and Beverage - Cooking spices",
+ "product_tax_code": "50171500A0000"
+ },
+ {
+ "description": "Food and Beverage - Alcoholic beverages - Beer/Malt Beverages",
+ "name": "Food and Beverage - Alcoholic beverages - Beer/Malt Beverages",
+ "product_tax_code": "50202201A0000"
+ },
+ {
+ "description": "Food and Beverage - Ice Cream, packaged",
+ "name": "Food and Beverage - Ice Cream, packaged",
+ "product_tax_code": "50192303A0000"
+ },
+ {
+ "description": "Electronic content bundle - Delivered electronically with permanent rights of usage and streamed",
+ "name": "Electronic content bundle - Delivered electronically with permanent rights of usage and streamed",
+ "product_tax_code": "55111500A9210"
+ },
+ {
+ "description": "Clothing - Roller skates or roller blades",
+ "name": "Clothing - Roller skates or roller blades",
+ "product_tax_code": "49221509A0000"
+ },
+ {
+ "description": "Clothing - Ice Skates",
+ "name": "Clothing - Ice Skates",
+ "product_tax_code": "49151602A0000"
+ },
+ {
+ "description": "Clothing - Life vests or preservers ",
+ "name": "Clothing - Life vests or preservers ",
+ "product_tax_code": "46161604A0000"
+ },
+ {
+ "description": "Clothing - Swim goggles",
+ "name": "Clothing - Swim goggles",
+ "product_tax_code": "49141606A0000"
+ },
+ {
+ "description": "Clothing - Bowling gloves",
+ "name": "Clothing - Bowling gloves",
+ "product_tax_code": "49211606A0002"
+ },
+ {
+ "description": "Fire Extinguishers",
+ "name": "Fire Extinguishers",
+ "product_tax_code": "46191601A0000"
+ },
+ {
+ "description": "Carbon Monoxide Detectors",
+ "name": "Carbon Monoxide Detectors",
+ "product_tax_code": "46191509A0001"
+ },
+ {
+ "description": "Ladder used for home emergency evacuation.",
+ "name": "Emergency/rescue ladder",
+ "product_tax_code": "30191501A0001"
+ },
+ {
+ "description": "Candles to be used a light source.",
+ "name": "Candles",
+ "product_tax_code": "39112604A0001"
+ },
+ {
+ "description": "Non-electric water container to store water for emergency usage.",
+ "name": "Water storage container",
+ "product_tax_code": "24111810A0001"
+ },
+ {
+ "description": "Duct Tape",
+ "name": "Duct Tape",
+ "product_tax_code": "31201501A0000"
+ },
+ {
+ "description": "Gas-powered chainsaw.",
+ "name": "Garden chainsaw",
+ "product_tax_code": "27112038A0000"
+ },
+ {
+ "description": "Chainsaw accessories include chains, lubricants, motor oil, chain sharpeners, bars, wrenches, carrying cases, repair parts, safety apparel.",
+ "name": "Chainsaw accessories",
+ "product_tax_code": "27112038A0001"
+ },
+ {
+ "description": "Shower curtain/liner used to keep water from escaping a showering area.",
+ "name": "Shower Curtain or Liner",
+ "product_tax_code": "30181607A0000"
+ },
+ {
+ "description": "Dish towels used for kitchenware drying.",
+ "name": "Dish towels",
+ "product_tax_code": "52121601A0000"
+ },
+ {
+ "description": "A bumper/liner that borders the interior walls/slats of the crib to help protect the baby.",
+ "name": "Crib bumpers/liners",
+ "product_tax_code": "56101804A0001"
+ },
+ {
+ "description": "A small mat/rug used to cover portion of bathroom floor.",
+ "name": "Bath Mats/rugs",
+ "product_tax_code": "52101507A0000"
+ },
+ {
+ "description": "A handheld computer that is capable of plotting graphs, solving simultaneous equations, and performing other tasks with variables.",
+ "name": "Graphing Calculators",
+ "product_tax_code": "44101808A0001"
+ },
+ {
+ "description": "Portable locks used by students in a school setting to prevent use, theft, vandalism or harm.",
+ "name": "Padlocks - Student",
+ "product_tax_code": "46171501A0001"
+ },
+ {
+ "description": "Domestic clothes washing appliances carrying Energy Star rating.",
+ "name": "Clothes Washing Machine - Energy Star",
+ "product_tax_code": "52141601A0000"
+ },
+ {
+ "description": "WaterSense labeled showerheads.",
+ "name": "Showerheads - WaterSense",
+ "product_tax_code": "30181801A0000"
+ },
+ {
+ "description": "Domestic dish washing appliances carrying Energy Star rating.",
+ "name": "Dishwashers - Energy Star",
+ "product_tax_code": "52141505A0000"
+ },
+ {
+ "description": "WaterSense labeled sprinkler body is the exterior shell that connects to the irrigation system piping and houses the spray nozzle that applies water on the landscape.",
+ "name": "Spray Water Sprinkler Bodies - WaterSense",
+ "product_tax_code": "21101803A0001"
+ },
+ {
+ "description": "Ropes and Cords",
+ "name": "Ropes and Cords",
+ "product_tax_code": "31151500A0000"
+ },
+ {
+ "description": "Light emitting diode (LED) bulbs carrying an Energy Star rating.",
+ "name": "LED Bulbs - Energy Star",
+ "product_tax_code": "39101628A0001"
+ },
+ {
+ "description": "WaterSense labeled bathroom sink faucets and accessories.",
+ "name": "Bathroom Faucets - WaterSense",
+ "product_tax_code": "30181702A0001"
+ },
+ {
+ "description": "Cables with industry standard connection and termination configurations used to connect various peripherals and equipment to computers.",
+ "name": "Computer Cables",
+ "product_tax_code": "43202222A0001"
+ },
+ {
+ "description": "Canned software delivered electronically that is used for non-recreational purposes, such as Antivirus, Database, Educational, Financial, Word processing, etc.",
+ "name": "Software - Prewritten, Electronic delivery - Non-recreational",
+ "product_tax_code": "43230000A1102"
+ },
+ {
+ "description": "Clothing - Baseball batting gloves",
+ "name": "Clothing - Baseball batting gloves",
+ "product_tax_code": "49211606A0001"
+ },
+ {
+ "description": "Cloud-based platform as a service (PaaS) - Business Use",
+ "name": "Cloud-based platform as a service (PaaS) - Business Use",
+ "product_tax_code": "81162100A9000"
+ },
+ {
+ "description": "Cloud-based Infrastructure as a service (IaaS) - Personal Use",
+ "name": "Cloud-based infrastructure as a service (IaaS) - Personal Use",
+ "product_tax_code": "81162200A0000"
+ },
+ {
+ "description": "Clothing - Costume Mask",
+ "name": "Clothing - Costume Mask",
+ "product_tax_code": "60122800A0000"
+ },
+ {
+ "description": "Clothing - Ski boots",
+ "name": "Clothing - Ski boots",
+ "product_tax_code": "53111900A0005"
+ },
+ {
+ "description": "Personal computer PC application design\r\n",
+ "name": "Personal computer PC application design\r\n",
+ "product_tax_code": "81111502A0000"
+ },
+ {
+ "description": "Network planning services\r\n",
+ "name": "Network planning services\r\n",
+ "product_tax_code": "81111706A0000"
+ },
+ {
+ "description": "ERP or database applications programming services\r\n",
+ "name": "ERP or database applications programming services\r\n",
+ "product_tax_code": "81111507A0000"
+ },
+ {
+ "description": "Content or data classification services\r\n",
+ "name": "Content or data classification services\r\n",
+ "product_tax_code": "81112009A0000"
+ },
+ {
+ "description": "Clothing - Prom Dress",
+ "name": "Clothing - Prom Dress",
+ "product_tax_code": "53101801A0003"
+ },
+ {
+ "description": "Clothing - Formal Dress",
+ "name": "Clothing - Formal Dress",
+ "product_tax_code": "53101801A0002"
+ },
+ {
+ "description": "Clothing - Handkerchiefs",
+ "name": "Clothing - Handkerchiefs",
+ "product_tax_code": "53102512A0000"
+ },
+ {
+ "description": "Clothing - Protective lens",
+ "name": "Clothing - Protective lens",
+ "product_tax_code": "46181811A0001"
+ },
+ {
+ "description": "Clothing - Body shaping garments",
+ "name": "Clothing - Body shaping garments",
+ "product_tax_code": "53102307A0000"
+ },
+ {
+ "description": "Clothing - Underpants",
+ "name": "Clothing - Underpants",
+ "product_tax_code": "53102303A0000"
+ },
+ {
+ "description": "Clothing - Waterproof boot",
+ "name": "Clothing - Waterproof boot",
+ "product_tax_code": "46181611A0000"
+ },
+ {
+ "description": "Electronic software documentation or user manuals - For prewritten software & delivered by load and leave",
+ "name": "Electronic software documentation or user manuals - Prewritten, load and leave delivery",
+ "product_tax_code": "55111601A1300"
+ },
+ {
+ "description": "Electronic software documentation or user manuals - For custom software & delivered on tangible media",
+ "name": "Electronic software documentation or user manuals - Custom, tangible media",
+ "product_tax_code": "55111601A2100"
+ },
+ {
+ "description": "Electronic software documentation or user manuals - For custom software & delivered by load and leave",
+ "name": "Electronic software documentation or user manuals - Custom, load and leave delivery",
+ "product_tax_code": "55111601A2300"
+ },
+ {
+ "description": "Electronic software documentation or user manuals - For custom software & delivered electronically",
+ "name": "Electronic software documentation or user manuals - Custom, electronic delivery",
+ "product_tax_code": "55111601A2200"
+ },
+ {
+ "description": "Electronic publications and music - Streamed",
+ "name": "Electronic publications and music - Streamed",
+ "product_tax_code": "55111500A1500"
+ },
+ {
+ "description": "Electronic publications and music - Delivered electronically with permanent rights of usage",
+ "name": "Electronic publications and music - Delivered electronically with permanent rights of usage",
+ "product_tax_code": "55111500A1210"
+ },
+ {
+ "description": "Electronic publications and music - Delivered electronically with less than permanent rights of usage",
+ "name": "Electronic publications and music - Delivered electronically with less than permanent rights of usage",
+ "product_tax_code": "55111500A1220"
+ },
+ {
+ "description": "Software - Custom & delivered on tangible media",
+ "name": "Software - Custom, tangible media",
+ "product_tax_code": "43230000A2100"
+ },
+ {
+ "description": "Software - Prewritten & delivered by digital keycode printed on tangible media",
+ "name": "Software - Prewritten, delivered by digital keycode printed on tangible media",
+ "product_tax_code": "43230000A1400"
+ },
+ {
+ "description": "Software - Prewritten & delivered by load and leave",
+ "name": "Software - Prewritten, load and leave delivery",
+ "product_tax_code": "43230000A1300"
+ },
+ {
+ "description": "Internet cloud storage service\r\n",
+ "name": "Internet cloud storage service\r\n",
+ "product_tax_code": "81111513A0000"
+ },
+ {
+ "description": "Computer or network or internet security\r\n",
+ "name": "Computer or network or internet security\r\n",
+ "product_tax_code": "81111801A0000"
+ },
+ {
+ "description": "Document scanning service\r\n",
+ "name": "Document scanning service\r\n",
+ "product_tax_code": "81112005A0000"
+ },
+ {
+ "description": "Cloud-based software as a service (SaaS) - Business Use",
+ "name": "Cloud-based software as a service (SaaS) - Business Use",
+ "product_tax_code": "81162000A9000"
+ },
+ {
+ "description": "Demining geographical or geospatial information system GIS\r\n",
+ "name": "Demining geographical or geospatial information system GIS\r\n",
+ "product_tax_code": "81111709A0000"
+ },
+ {
+ "description": "Content or data standardization services\r\n",
+ "name": "Content or data standardization services\r\n",
+ "product_tax_code": "81112007A0000"
+ },
+ {
+ "description": "Data processing or preparation services\r\n",
+ "name": "Data processing or preparation services\r\n",
+ "product_tax_code": "81112002A0000"
+ },
+ {
+ "description": "Database analysis service\r\n",
+ "name": "Database analysis service\r\n",
+ "product_tax_code": "81111806A0000"
+ },
+ {
+ "description": "Electronic software documentation or user manuals - For prewritten software & delivered on tangible media",
+ "name": "Electronic software documentation or user manuals - Prewritten, tangible media",
+ "product_tax_code": "55111601A1100"
+ },
+ {
+ "description": "Cloud-based software as a service (SaaS) - Personal Use",
+ "name": "Cloud-based software as a service (SaaS) - Personal Use",
+ "product_tax_code": "81162000A0000"
+ },
+ {
+ "description": "Clothing - Costume",
+ "name": "Clothing - Costume",
+ "product_tax_code": "60141401A0000"
+ },
+ {
+ "description": "Online data processing service\r\n",
+ "name": "Online data processing service\r\n",
+ "product_tax_code": "81112001A0000"
+ },
+ {
+ "description": "System usability services\r\n",
+ "name": "System usability services\r\n",
+ "product_tax_code": "81111820A0000"
+ },
+ {
+ "description": "Services that provide both essential proactive and reactive operations and maintenance support.",
+ "name": "IT Support Services",
+ "product_tax_code": "81111811A0000"
+ },
+ {
+ "description": "System analysis service\r\n",
+ "name": "System analysis service\r\n",
+ "product_tax_code": "81111808A0000"
+ },
+ {
+ "description": "Data storage service\r\n",
+ "name": "Data storage service\r\n",
+ "product_tax_code": "81112006A0000"
+ },
+ {
+ "description": "Quality assurance services\r\n",
+ "name": "Quality assurance services\r\n",
+ "product_tax_code": "81111819A0000"
+ },
+ {
+ "description": "Software - Custom & delivered by load & leave",
+ "name": "Software - Custom, load and leave delivery",
+ "product_tax_code": "43230000A2300"
+ },
+ {
+ "description": "Food and Beverage - Meat Sticks, Meat Jerky",
+ "name": "Food and Beverage - Meat Sticks, Meat Jerky",
+ "product_tax_code": "50112000A0000"
+ },
+ {
+ "description": "Clothing - Synthetic Fur Poncho or Cape",
+ "name": "Clothing - Synthetic Fur Poncho or Cape",
+ "product_tax_code": "53101806A0002"
+ },
+ {
+ "description": "Clothing - Wetsuit",
+ "name": "Clothing - Wetsuit",
+ "product_tax_code": "49141506A0000"
+ },
+ {
+ "description": "Cloud-based business process as a service - Business Use",
+ "name": "Cloud-based business process as a service - Business Use",
+ "product_tax_code": "81162300A9000"
+ },
+ {
+ "description": "Cloud-based infrastructure as a service (IaaS) - Business Use",
+ "name": "Cloud-based infrastructure as a service (IaaS) - Business Use",
+ "product_tax_code": "81162200A9000"
+ },
+ {
+ "description": "Clothing - Belt Buckle",
+ "name": "Clothing - Belt Buckle",
+ "product_tax_code": "53102501A0001"
+ },
+ {
+ "description": "Clothing - Shower Cap",
+ "name": "Clothing - Shower Cap",
+ "product_tax_code": "53131601A0000"
+ },
+ {
+ "description": "Clothing - Eye shield garters",
+ "name": "Clothing - Eye shield garters",
+ "product_tax_code": "46181809A0001"
+ },
+ {
+ "description": "Clothing - Socks",
+ "name": "Clothing - Socks",
+ "product_tax_code": "53102402A0000"
+ },
+ {
+ "description": "Clothing - Stockings",
+ "name": "Clothing - Stockings",
+ "product_tax_code": "53102401A0000"
+ },
+ {
+ "description": "Food and Beverage - Meat and meat products",
+ "name": "Food and Beverage - Meat and meat products",
+ "product_tax_code": "50110000A0000"
+ },
+ {
+ "description": "Clothing - Slips",
+ "name": "Clothing - Slips",
+ "product_tax_code": "53102302A0000"
+ },
+ {
+ "description": "Clothing - Goggle protective covers",
+ "name": "Clothing - Goggle protective covers",
+ "product_tax_code": "46181808A0001"
+ },
+ {
+ "description": "Clothing - Goggles",
+ "name": "Clothing - Goggles",
+ "product_tax_code": "46181804A0001"
+ },
+ {
+ "description": "Clothing - Football receiver gloves",
+ "name": "Clothing - Football receiver gloves",
+ "product_tax_code": "49211606A0004"
+ },
+ {
+ "description": "Disaster recovery services",
+ "name": "Disaster recovery services",
+ "product_tax_code": "81112004A0000"
+ },
+ {
+ "description": "Clothing - Mountain climbing boot",
+ "name": "Clothing - Mountain climbing boot",
+ "product_tax_code": "46181613A0000"
+ },
+ {
+ "description": "Software maintenance and support - Mandatory maintenance and support charges for prewritten software including items delivered on tangible media",
+ "name": "Software maintenance and support - Mandatory, prewritten, tangible media",
+ "product_tax_code": "81112200A1110"
+ },
+ {
+ "description": "Clothing - Military boot",
+ "name": "Clothing - Military boot",
+ "product_tax_code": "46181612A0000"
+ },
+ {
+ "description": "Carbonated beverages marketed as energy drinks, carrying a Supplement Facts Label, that contain a blend of energy enhancing vitamins, minerals, herbals, stimulants, etc.",
+ "name": "Energy Beverages - Carbonated - with Supplement Facts Label",
+ "product_tax_code": "50202309A0001"
+ },
+ {
+ "description": "Non-carbonated beverages marketed as energy drinks, carrying a Supplement Facts Label, that contain a blend of energy enhancing vitamins, minerals, herbals, stimulants, etc.",
+ "name": "Energy Beverages - Non-Carbonated - with Supplement Facts Label",
+ "product_tax_code": "50202309A0000"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with tangible personal property, with the food comprising 90% or more of the overall value of the bundle, where all food consists of candy (not containing flour).",
+ "name": "Food/TPP Bundle - with Food 90% or more - Food is all Candy",
+ "product_tax_code": "50193400A0001"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with tangible personal property, with the food comprising less 90% or more of the overall value of the bundle.",
+ "name": "Food/TPP Bundle - with Food 90% or more",
+ "product_tax_code": "50193400A0000"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with tangible personal property, with the food comprising between 76% and 89% of the overall value of the bundle, where all food consists of candy (not containing flour).",
+ "name": "Food/TPP Bundle - with Food between 76% and 89% - Food is all Candy",
+ "product_tax_code": "50193400A0005"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with tangible personal property, with the food comprising between 76% and 89% of the overall value of the bundle.",
+ "name": "Food/TPP Bundle - with Food between 76% and 89%",
+ "product_tax_code": "50193400A0004"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with tangible personal property, with the food comprising between 50% and 75% of the overall value of the bundle, where all food consists of candy (not containing flour).",
+ "name": "Food/TPP Bundle - with Food between 50% and 75% - Food is all Candy",
+ "product_tax_code": "50193400A0003"
+ },
+ {
+ "description": "Food bundle or basket containing food staples combined with tangible personal property, with the food comprising between 50% and 75% of the overall value of the bundle.",
+ "name": "Food/TPP Bundle - with Food between 50% and 75%",
+ "product_tax_code": "50193400A0002"
+ },
+ {
+ "description": "Food/TPP Bundle - with Food less than 50%",
+ "name": "Food/TPP Bundle - with Food less than 50%",
+ "product_tax_code": "50193400A0006"
+ },
+ {
+ "description": "Ready to drink beverages, not containing milk, formulated and labled for their nutritional value, such as increased caloric or protein intake and containing natrual or artificial sweeteners.",
+ "name": "Nutritional Supplement/protein drinks, shakes - contains no milk",
+ "product_tax_code": "50501703A0000"
+ },
+ {
+ "description": "Ready to drink beverages, containing milk, formulated and labled for their nutritional value, such as increased caloric or protein intake.",
+ "name": "Nutritional Supplement/protein drinks, shakes - contains milk",
+ "product_tax_code": "50501703A0001"
+ },
+ {
+ "description": "Powdered mixes to be reconstituted into a drinkable beverage using water.",
+ "name": "Powdered Drink Mixes - to be mixed with water",
+ "product_tax_code": "50202311A0000"
+ },
+ {
+ "description": "Powdered mixes to be reconstituted into a drinkable beverage using milk or a milk substitute.",
+ "name": "Powdered Drink Mixes - to be mixed with milk",
+ "product_tax_code": "50202311A0001"
+ },
+ {
+ "description": "Food and Beverage - Granola Bars, Cereal Bars, Energy Bars, Protein Bars containing no flour",
+ "name": "Food and Beverage - Granola Bars, Cereal Bars, Energy Bars, Protein Bars containing no flour",
+ "product_tax_code": "50221202A0002"
+ },
+ {
+ "description": "Food and Beverage - Granola Bars, Cereal Bars, Energy Bars, Protein Bars containing flour",
+ "name": "Food and Beverage - Granola Bars, Cereal Bars, Energy Bars, Protein Bars containing flour",
+ "product_tax_code": "50221202A0001"
+ },
+ {
+ "description": "Nutritional supplement in powder form, dairy based or plant based, focused on increasing ones intake of protein for various benefits.",
+ "name": "Protein Powder",
+ "product_tax_code": "50501703A0002"
+ },
+ {
+ "description": "Ready to drink non-carbonated beverage containing tea with natural or artificial sweeteners.",
+ "name": "Bottled tea - non-carbonated - sweetened",
+ "product_tax_code": "50201712A0003"
+ },
+ {
+ "description": "Ready to drink non-carbonated beverage containing tea without natural or artificial sweeteners.",
+ "name": "Bottled tea - non-carbonated - unsweetened",
+ "product_tax_code": "50201712A0000"
+ },
+ {
+ "description": "Ready to drink carbonated beverage containing tea with natural or artificial sweeteners.",
+ "name": "Bottled tea - carbonated - sweetened",
+ "product_tax_code": "50201712A0002"
+ },
+ {
+ "description": "Ready to drink carbonated beverage containing tea and without any natural or artificial sweeteners.",
+ "name": "Bottled tea - carbonated - unsweetened",
+ "product_tax_code": "50201712A0001"
+ },
+ {
+ "description": "Ready to drink coffee based beverage containing milk or milk substitute.",
+ "name": "Bottled coffee - containing milk or milk substitute",
+ "product_tax_code": "50201708A0002"
+ },
+ {
+ "description": "Ready to drink coffee based beverage not containing milk, containing natural or artificial sweetener.",
+ "name": "Bottled coffee - no milk - sweetened",
+ "product_tax_code": "50201708A0001"
+ },
+ {
+ "description": "Ready to drink coffee based beverage containing neither milk nor natural or artificial sweeteners.",
+ "name": "Bottled coffee - no milk - unsweetened",
+ "product_tax_code": "50201708A0000"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 51 - 69% natural vegetable juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 51-69% vegetable juice",
+ "product_tax_code": "50202306A0008"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 1 - 9% natural fruit juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 1-9% fruit juice",
+ "product_tax_code": "50202306A0001"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 70 - 99% natural fruit juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 70-99% fruit juice",
+ "product_tax_code": "50202304A0010"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 51 - 69% natural vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 51-69% vegetable juice",
+ "product_tax_code": "50202304A0009"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 25 - 50% natural vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 25-50% vegetable juice",
+ "product_tax_code": "50202304A0007"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 10 - 24% natural fruit juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 10-24% fruit juice",
+ "product_tax_code": "50202304A0004"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 70 - 99% natural vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 70-99% vegetable juice",
+ "product_tax_code": "50202304A0011"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and zero natural fruit or vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - No fruit or vegetable juice",
+ "product_tax_code": "50202304A0001"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 51 - 69% natural fruit juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 51-69% fruit juice",
+ "product_tax_code": "50202304A0008"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 1 - 9% natural vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 1 -9% vegetable juice",
+ "product_tax_code": "50202304A0003"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 1 - 9% natural fruit juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 1-9% fruit juice",
+ "product_tax_code": "50202304A0002"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 10 - 24% natural vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 10-24% vegetable juice",
+ "product_tax_code": "50202304A0005"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 25 - 50% natural fruit juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 25-50% fruit juice",
+ "product_tax_code": "50202304A0006"
+ },
+ {
+ "description": "Non-carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 100% natural fruit or vegetable juice. This does not include flavored water. This does include sweetened cocktail mixes that can be combined with alcohol. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Non-Carbonated - 100% fruit or vegetable juice",
+ "product_tax_code": "50202304A0000"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and zero natural fruit or vegetable juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - No fruit or vegetable juice",
+ "product_tax_code": "50202306A0000"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 70 - 99% natural vegetable juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 70-99% vegetable juice",
+ "product_tax_code": "50202306A0010"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 70 - 99% natural fruit juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 70-99% fruit juice",
+ "product_tax_code": "50202306A0009"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 51 - 69% natural fruit juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 51-69% fruit juice",
+ "product_tax_code": "50202306A0007"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 25 - 50% natural vegetable juice. This does not flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 25-50% vegetable juice",
+ "product_tax_code": "50202306A0006"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 25 - 50% natural fruit juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 25-50% fruit juice",
+ "product_tax_code": "50202306A0005"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 100% natural fruit or vegetable juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 100% fruit or vegetable juice",
+ "product_tax_code": "50202306A0011"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 10 - 24% natural vegetable juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 10-24% vegetable juice",
+ "product_tax_code": "50202306A0004"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 10 - 24% natural fruit juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 10-24% fruit juice",
+ "product_tax_code": "50202306A0003"
+ },
+ {
+ "description": "Carbonated nonalcoholic beverages that contain natural or artificial sweeteners, and 1 - 9% natural vegetable juice. This does not include flavored carbonated water. This does include beverages marketed as energy drinks that carry a Nutrition Facts label and contain a blend of energy enhancing ingredients.",
+ "name": "Soft Drinks - Carbonated - 1 -9% vegetable juice",
+ "product_tax_code": "50202306A0002"
+ },
+ {
+ "description": "Bottled Water for human consumption, unsweetened, non-carbonated. Does not include distilled water.",
+ "name": "Bottled Water",
+ "product_tax_code": "50202301A0000"
+ },
+ {
+ "description": "Bottled Water for human consumption, containing natural or artificial sweeteners, non-carbonated.",
+ "name": "Bottled Water - Flavored",
+ "product_tax_code": "50202301A0001"
+ },
+ {
+ "description": "Bottled Water for human consumption, unsweetened, carbonated naturally. Includes carbonated waters containing only natural flavors or essences.",
+ "name": "Bottled Water - Carbonated Naturally",
+ "product_tax_code": "50202301A0003"
+ },
+ {
+ "description": "Bottled Water for human consumption, unsweetened, carbonated artificially during bottling process. Includes carbonated waters containing only natural flavors or essences.",
+ "name": "Bottled Water - Carbonated Artificially",
+ "product_tax_code": "50202301A0002"
+ },
+ {
+ "description": "Bottled Water for human consumption, containing natural or artificial sweeteners, carbonated.",
+ "name": "Bottled Water - Carbonated - Sweetened",
+ "product_tax_code": "50202301A0004"
+ },
+ {
+ "description": "Clothing - Sequins for use in clothing",
+ "name": "Clothing - Sequins for use in clothing",
+ "product_tax_code": "60123900A0000"
+ },
+ {
+ "description": "Clothing - Synthetic Fur Gloves",
+ "name": "Clothing - Synthetic Fur Gloves",
+ "product_tax_code": "53102503A0002"
+ },
+ {
+ "description": "Clothing - Synthetic Fur Coat or Jacket",
+ "name": "Clothing - Synthetic Fur Coat or Jacket",
+ "product_tax_code": "53101800A0002"
+ },
+ {
+ "description": "Clothing - Fur Hat",
+ "name": "Clothing - Fur Hat",
+ "product_tax_code": "53102504A0001"
+ },
+ {
+ "description": "Clothing - Fur Coat or Jacket",
+ "name": "Clothing - Fur Coat or Jacket",
+ "product_tax_code": "53101800A0001"
+ },
+ {
+ "description": "Cloud-based business process as a service",
+ "name": "Cloud-based business process as a service - Personal Use",
+ "product_tax_code": "81162300A0000"
+ },
+ {
+ "description": "Clothing - Shoulder pads for sports",
+ "name": "Clothing - Shoulder pads for sports",
+ "product_tax_code": "46181506A0002"
+ },
+ {
+ "description": "Mainframe software applications design\r\n",
+ "name": "Mainframe software applications design\r\n",
+ "product_tax_code": "81111501A0000"
+ },
+ {
+ "description": "Clothing - Safety shoes",
+ "name": "Clothing - Safety shoes",
+ "product_tax_code": "46181605A0000"
+ },
+ {
+ "description": "Clothing - Protective hood",
+ "name": "Clothing - Protective hood",
+ "product_tax_code": "46181710A0001"
+ },
+ {
+ "description": "Clothing - Face protection kit",
+ "name": "Clothing - Face protection kit",
+ "product_tax_code": "46181709A0001"
+ },
+ {
+ "description": "Clothing - Protective hair net",
+ "name": "Clothing - Protective hair net",
+ "product_tax_code": "46181708A0001"
+ },
+ {
+ "description": "Clothing - Facial shields parts or accessories",
+ "name": "Clothing - Facial shields parts or accessories",
+ "product_tax_code": "46181707A0001"
+ },
+ {
+ "description": "Clothing - Safety helmets",
+ "name": "Clothing - Safety helmets",
+ "product_tax_code": "46181704A0001"
+ },
+ {
+ "description": "Clothing - Poncho",
+ "name": "Clothing - Poncho",
+ "product_tax_code": "53101806A0000"
+ },
+ {
+ "description": "Clothing - Protective insole",
+ "name": "Clothing - Protective insole",
+ "product_tax_code": "46181609A0000"
+ },
+ {
+ "description": "Clothing - Protective clogs",
+ "name": "Clothing - Protective clogs",
+ "product_tax_code": "46181607A0000"
+ },
+ {
+ "description": "Clothing - Waterproof jacket or raincoat",
+ "name": "Clothing - Waterproof jacket or raincoat",
+ "product_tax_code": "46181543A0000"
+ },
+ {
+ "description": "Systems architecture\r\n",
+ "name": "Systems architecture\r\n",
+ "product_tax_code": "81111705A0000"
+ },
+ {
+ "description": "System installation service\r\n",
+ "name": "System installation service\r\n",
+ "product_tax_code": "81111809A0000"
+ },
+ {
+ "description": "Software maintenance and support - Mandatory maintenance and support charges for custom software including items delivered by load and leave",
+ "name": "Software maintenance and support - Mandatory, custom, load and leave delivery",
+ "product_tax_code": "81112200A2310"
+ },
+ {
+ "description": "Software coding service\r\n",
+ "name": "Software coding service\r\n",
+ "product_tax_code": "81111810A0000"
+ },
+ {
+ "description": "Software - Custom & delivered electronically",
+ "name": "Software - Custom, electronic delivery",
+ "product_tax_code": "43230000A2200"
+ },
+ {
+ "description": "Bathing suits and swim suits",
+ "name": "Clothing - Swimwear",
+ "product_tax_code": "20041"
+ },
+ {
+ "description": "Miscellaneous services which are not subject to a service-specific tax levy. This category will only treat services as taxable if the jurisdiction taxes services generally.",
+ "name": "General Services",
+ "product_tax_code": "19000"
+ },
+ {
+ "description": "Services provided to educate users on the proper use of a product. Live training in person",
+ "name": "Training Services - Live",
+ "product_tax_code": "19004"
+ },
+ {
+ "description": "Admission charges associated with entry to an event.",
+ "name": "Admission Services",
+ "product_tax_code": "19003"
+ },
+ {
+ "description": "Service of providing usage of a parking space.",
+ "name": "Parking Services",
+ "product_tax_code": "19002"
+ },
+ {
+ "description": "A charge separately stated from any sale of the product itself for the installation of tangible personal property. This a labor charge, with any non-separately stated property transferred in performing the service considered inconsequential.",
+ "name": "Installation Services",
+ "product_tax_code": "10040"
+ },
+ {
+ "description": "Services rendered for advertising which do not include the exchange of tangible personal property.",
+ "name": "Advertising Services",
+ "product_tax_code": "19001"
+ },
+ {
+ "description": "Digital products transferred electronically, meaning obtained by the purchaser by means other than tangible storage media.",
+ "name": "Digital Goods",
+ "product_tax_code": "31000"
+ },
+ {
+ "description": " All human wearing apparel suitable for general use",
+ "name": "Clothing",
+ "product_tax_code": "20010"
+ },
+ {
+ "description": "An over-the-counter drug is a substance that contains a label identifying it as a drug and including a \"drug facts\" panel or a statement of active ingredients, that can be obtained without a prescription. A drug can be intended for internal (ingestible, implant, injectable) or external (topical) application to the human body.",
+ "name": "Over-the-Counter Drugs",
+ "product_tax_code": "51010"
+ },
+ {
+ "description": "A substance that can only be obtained via a prescription of a licensed professional. A drug is a compound, substance, or preparation, and any component thereof, not including food or food ingredients, dietary supplements, or alcoholic beverages, that is: recognized in the official United States pharmacopoeia, official homeopathic pharmacopoeia of the United States, or official national formulary, and supplement to any of them; intended for use in the diagnosis, cure, mitigation, treatment, or prevention of disease; or intended to affect the structure or any function of the body. A drug can be intended for internal (ingestible, implant, injectable) or external (topical) application to the human body.",
+ "name": "Prescription Drugs",
+ "product_tax_code": "51020"
+ },
+ {
+ "description": "Food for humans consumption, unprepared",
+ "name": "Food & Groceries",
+ "product_tax_code": "40030"
+ },
+ {
+ "description": "Pre-written software, delivered electronically, but access remotely.",
+ "name": "Software as a Service",
+ "product_tax_code": "30070"
+ },
+ {
+ "description": "Periodicals, printed, sold by subscription",
+ "name": "Magazines & Subscriptions",
+ "product_tax_code": "81300"
+ },
+ {
+ "description": "Books, printed",
+ "name": "Books",
+ "product_tax_code": "81100"
+ },
+ {
+ "description": "Periodicals, printed, sold individually",
+ "name": "Magazine",
+ "product_tax_code": "81310"
+ },
+ {
+ "description": "Textbooks, printed",
+ "name": "Textbook",
+ "product_tax_code": "81110"
+ },
+ {
+ "description": "Religious books and manuals, printed",
+ "name": "Religious books",
+ "product_tax_code": "81120"
+ },
+ {
+ "description": "Non-food dietary supplements",
+ "name": "Supplements",
+ "product_tax_code": "40020"
+ },
+ {
+ "description": "Candy",
+ "name": "Candy",
+ "product_tax_code": "40010"
+ },
+ {
+ "description": "Soft drinks. Soda and similar drinks. Does not include water, juice, or milk.",
+ "name": "Soft Drinks",
+ "product_tax_code": "40050"
+ },
+ {
+ "description": "Bottled water for human consumption.",
+ "name": "Bottled Water",
+ "product_tax_code": "40060"
+ },
+ {
+ "description": "Ready to eat foods intended to be consumed on site by humans. Foods not considered to be Food & Grocery (not food for home consumption or food which requires further preparation to consume).",
+ "name": "Prepared Foods",
+ "product_tax_code": "41000"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py
index 24ab1cf..9c183af 100644
--- a/erpnext/regional/united_states/setup.py
+++ b/erpnext/regional/united_states/setup.py
@@ -3,12 +3,41 @@
from __future__ import unicode_literals
import frappe
+import os
+import json
+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:
+ setup_company_independent_fixtures(patch=patch)
+
+def setup_company_independent_fixtures(company=None, patch=True):
+ add_product_tax_categories()
make_custom_fields()
+ add_permissions()
+ frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
add_print_formats()
+# Product Tax categories imported from taxjar api
+def add_product_tax_categories():
+ with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
+ tax_categories = json.loads(f.read())
+ create_tax_categories(tax_categories['categories'])
+
+def create_tax_categories(data):
+ for d in data:
+ tax_category = frappe.new_doc('Product Tax Category')
+ tax_category.description = d.get("description")
+ tax_category.product_tax_code = d.get("product_tax_code")
+ tax_category.category_name = d.get("name")
+ try:
+ tax_category.db_insert()
+ except frappe.DuplicateEntryError:
+ pass
+
+
def make_custom_fields(update=True):
custom_fields = {
'Supplier': [
@@ -30,10 +59,29 @@
'Quotation': [
dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
label='Is customer exempted from sales tax?')
+ ],
+ 'Sales Invoice Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
+ label='Product Tax Category', fetch_from='item_code.product_tax_category'),
+ dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
+ label='Tax Collectable', read_only=1),
+ dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
+ label='Taxable Amount', read_only=1)
+ ],
+ 'Item': [
+ dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
+ label='Product Tax Category')
]
}
create_custom_fields(custom_fields, update=update)
+def add_permissions():
+ doctype = "Product Tax Category"
+ for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
+ add_permission(doctype, role, 0)
+ update_permission_property(doctype, role, 0, 'write', 1)
+ update_permission_property(doctype, role, 0, 'create', 1)
+
def add_print_formats():
frappe.reload_doc("regional", "print_format", "irs_1099_form")
frappe.db.set_value("Print Format", "IRS 1099 Form", "disabled", 0)
diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py
index 513570e..19e9a35 100644
--- a/erpnext/regional/united_states/test_united_states.py
+++ b/erpnext/regional/united_states/test_united_states.py
@@ -1,8 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
+
+import frappe
+
from erpnext.regional.report.irs_1099.irs_1099 import execute as execute_1099_report
diff --git a/erpnext/restaurant/doctype/restaurant/restaurant.py b/erpnext/restaurant/doctype/restaurant/restaurant.py
index 0bb7b69..486afc3 100644
--- a/erpnext/restaurant/doctype/restaurant/restaurant.py
+++ b/erpnext/restaurant/doctype/restaurant/restaurant.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class Restaurant(Document):
pass
diff --git a/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py b/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py
index ec62ba2..5b78bb2 100644
--- a/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py
+++ b/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'restaurant',
@@ -14,4 +16,4 @@
'items': ['Restaurant Reservation', 'Sales Invoice']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/restaurant/doctype/restaurant/test_restaurant.js b/erpnext/restaurant/doctype/restaurant/test_restaurant.js
index 26de5d0..8fe4e7b 100644
--- a/erpnext/restaurant/doctype/restaurant/test_restaurant.js
+++ b/erpnext/restaurant/doctype/restaurant/test_restaurant.js
@@ -18,7 +18,7 @@
frappe.run_serially([
// insert a new Restaurant
- () => frappe.tests.setup_doctype('Customer', customer),
+ () => frappe.tests.setup_doctype('Customer', customer),
() => {
return frappe.tests.make('Restaurant', [
// values to be set
diff --git a/erpnext/restaurant/doctype/restaurant/test_restaurant.py b/erpnext/restaurant/doctype/restaurant/test_restaurant.py
index 3ba7f57..574cd1f 100644
--- a/erpnext/restaurant/doctype/restaurant/test_restaurant.py
+++ b/erpnext/restaurant/doctype/restaurant/test_restaurant.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
test_records = [
diff --git a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py
index 83020b6..632f485 100644
--- a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py
+++ b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class RestaurantMenu(Document):
def validate(self):
for d in self.items:
@@ -57,5 +59,3 @@
price_list.save()
return price_list
-
-
diff --git a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py b/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py
index 29f95fd..00cbf35 100644
--- a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py
+++ b/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.py
@@ -3,9 +3,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
test_records = [
dict(doctype='Item', item_code='Food Item 1',
item_group='Products', is_stock_item=0),
diff --git a/erpnext/restaurant/doctype/restaurant_menu_item/restaurant_menu_item.py b/erpnext/restaurant/doctype/restaurant_menu_item/restaurant_menu_item.py
index cc86bb3..5d095f4 100644
--- a/erpnext/restaurant/doctype/restaurant_menu_item/restaurant_menu_item.py
+++ b/erpnext/restaurant/doctype/restaurant_menu_item/restaurant_menu_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class RestaurantMenuItem(Document):
pass
diff --git a/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py b/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py
index 357deaa..1ed5921 100644
--- a/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py
+++ b/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py
@@ -3,11 +3,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.model.document import Document
+
+import json
+
+import frappe
from frappe import _
+from frappe.model.document import Document
+
from erpnext.controllers.queries import item_query
+
class RestaurantOrderEntry(Document):
pass
diff --git a/erpnext/restaurant/doctype/restaurant_order_entry_item/restaurant_order_entry_item.py b/erpnext/restaurant/doctype/restaurant_order_entry_item/restaurant_order_entry_item.py
index e0c051b..ee8928b 100644
--- a/erpnext/restaurant/doctype/restaurant_order_entry_item/restaurant_order_entry_item.py
+++ b/erpnext/restaurant/doctype/restaurant_order_entry_item/restaurant_order_entry_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class RestaurantOrderEntryItem(Document):
pass
diff --git a/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.py b/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.py
index f96de44..f6d2a7c 100644
--- a/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.py
+++ b/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
+
from datetime import timedelta
+
+from frappe.model.document import Document
from frappe.utils import get_datetime
+
class RestaurantReservation(Document):
def validate(self):
if not self.reservation_end_time:
diff --git a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.py b/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.py
index 71681b2..885da72 100644
--- a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.py
+++ b/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestRestaurantReservation(unittest.TestCase):
pass
diff --git a/erpnext/restaurant/doctype/restaurant_table/restaurant_table.py b/erpnext/restaurant/doctype/restaurant_table/restaurant_table.py
index d5ea9d5..0b5d635 100644
--- a/erpnext/restaurant/doctype/restaurant_table/restaurant_table.py
+++ b/erpnext/restaurant/doctype/restaurant_table/restaurant_table.py
@@ -3,10 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, re
+
+import re
+
from frappe.model.document import Document
from frappe.model.naming import make_autoname
+
class RestaurantTable(Document):
def autoname(self):
prefix = re.sub('-+', '-', self.restaurant.replace(' ', '-'))
diff --git a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.py b/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.py
index ffdb6f7..44059ae 100644
--- a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.py
+++ b/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
test_records = [
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 2849466..4b0bbd5 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -111,20 +111,20 @@
}
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'}
- frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if(!frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(frm);
// custom buttons
- frm.add_custom_button(__('Accounting Ledger'), function() {
- frappe.set_route('query-report', 'General Ledger',
- {party_type:'Customer', party:frm.doc.name});
- });
- frm.add_custom_button(__('Accounts Receivable'), function() {
+ frm.add_custom_button(__('Accounts Receivable'), function () {
frappe.set_route('query-report', 'Accounts Receivable', {customer:frm.doc.name});
- });
+ }, __('View'));
+
+ frm.add_custom_button(__('Accounting Ledger'), function () {
+ frappe.set_route('query-report', 'General Ledger',
+ {party_type: 'Customer', party: frm.doc.name});
+ }, __('View'));
frm.add_custom_button(__('Pricing Rule'), function () {
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
@@ -160,4 +160,3 @@
}
});
-
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index cd94ee1..e811435 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -20,6 +20,7 @@
"tax_withholding_category",
"default_bank_account",
"lead_name",
+ "opportunity_name",
"image",
"column_break0",
"account_manager",
@@ -267,6 +268,7 @@
"options": "fa fa-map-marker"
},
{
+ "depends_on": "eval: !doc.__islocal",
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML",
@@ -283,6 +285,7 @@
"width": "50%"
},
{
+ "depends_on": "eval: !doc.__islocal",
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML",
@@ -493,14 +496,28 @@
"fieldtype": "Link",
"label": "Tax Withholding Category",
"options": "Tax Withholding Category"
+ },
+ {
+ "fieldname": "opportunity_name",
+ "fieldtype": "Link",
+ "label": "From Opportunity",
+ "no_copy": 1,
+ "options": "Opportunity",
+ "print_hide": 1
}
],
"icon": "fa fa-user",
"idx": 363,
"image_field": "image",
"index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-01-28 12:54:57.258959",
+ "links": [
+ {
+ "group": "Allowed Items",
+ "link_doctype": "Party Specific Item",
+ "link_fieldname": "party"
+ }
+ ],
+ "modified": "2021-09-06 17:38:54.196663",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index abf146c..7adf2cd 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -2,20 +2,26 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from frappe.model.naming import set_name_by_naming_series
-from frappe import _, msgprint
+
+import frappe
import frappe.defaults
-from frappe.utils import flt, cint, cstr, today, get_formatted_email
+from frappe import _, msgprint
+from frappe.contacts.address_and_contact import (
+ delete_contact_and_address,
+ load_address_and_contact,
+)
from frappe.desk.reportview import build_match_conditions, get_filters_cond
-from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
-from frappe.model.rename_doc import update_linked_doctypes
from frappe.model.mapper import get_mapped_doc
+from frappe.model.naming import set_name_by_naming_series, set_name_from_naming_options
+from frappe.model.rename_doc import update_linked_doctypes
+from frappe.utils import cint, cstr, flt, get_formatted_email, today
from frappe.utils.user import get_users_with_role
+from erpnext.accounts.party import get_dashboard_info, validate_party_accounts
+from erpnext.utilities.transaction_base import TransactionBase
+
class Customer(TransactionBase):
def get_feed(self):
@@ -34,8 +40,10 @@
cust_master_name = frappe.defaults.get_global_default('cust_master_name')
if cust_master_name == 'Customer Name':
self.name = self.get_customer_name()
- else:
+ 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)
def get_customer_name(self):
@@ -150,8 +158,14 @@
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'):
- make_address(self)
+ address = make_address(self)
+ address_display = get_address_display(address.name)
+
+ self.db_set("customer_primary_address", address.name)
+ self.db_set("primary_address", address_display)
def update_lead_status(self):
'''If Customer created from Lead, update lead status to "Converted"
@@ -246,9 +260,15 @@
def on_trash(self):
if self.customer_primary_contact:
- frappe.db.sql("""update `tabCustomer`
- set customer_primary_contact=null, mobile_no=null, email_id=null
- where name=%s""", self.name)
+ frappe.db.sql("""
+ UPDATE `tabCustomer`
+ SET
+ customer_primary_contact=null,
+ customer_primary_address=null,
+ mobile_no=null,
+ email_id=null,
+ primary_address=null
+ WHERE name=%(name)s""", {"name": self.name})
delete_contact_and_address('Customer', self.name)
if self.lead_name:
diff --git a/erpnext/selling/doctype/customer/regional/india.js b/erpnext/selling/doctype/customer/regional/india.js
index edb8383..cad9a27 100644
--- a/erpnext/selling/doctype/customer/regional/india.js
+++ b/erpnext/selling/doctype/customer/regional/india.js
@@ -1,3 +1,3 @@
{% include "erpnext/regional/india/party.js" %}
-erpnext.setup_gst_reminder_button('Customer')
\ No newline at end of file
+erpnext.setup_gst_reminder_button('Customer')
diff --git a/erpnext/selling/doctype/customer/test_customer.js b/erpnext/selling/doctype/customer/test_customer.js
deleted file mode 100644
index 65b81af..0000000
--- a/erpnext/selling/doctype/customer/test_customer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Customer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Customer
- () => frappe.tests.make('Customer', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index b1a5b52..fd1db8f 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -3,13 +3,14 @@
from __future__ import unicode_literals
-import frappe
import unittest
-from erpnext.accounts.party import get_due_date
+import frappe
from frappe.test_runner import make_test_records
-from erpnext.exceptions import PartyFrozen, PartyDisabled
from frappe.utils import flt
+
+from erpnext.accounts.party import get_due_date
+from erpnext.exceptions import PartyDisabled, PartyFrozen
from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
from erpnext.tests.utils import create_test_contact_and_address
@@ -19,6 +20,7 @@
from six import iteritems
+
class TestCustomer(unittest.TestCase):
def setUp(self):
if not frappe.get_value('Item', '_Test Item'):
@@ -253,10 +255,10 @@
return get_customer_outstanding('_Test Customer', '_Test Company')
def test_customer_credit_limit(self):
- from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
- from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
+ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+ 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')
@@ -352,3 +354,26 @@
'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
+ })
+
+ customer.insert()
+ customer_name = customer.name
+ else:
+ customer_name = frappe.db.get_value("Customer", customer_name)
+
+ return customer_name
diff --git a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py
index 60a4a9a..53bcc1b 100644
--- a/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py
+++ b/erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class CustomerCreditLimit(Document):
pass
diff --git a/erpnext/selling/doctype/industry_type/industry_type.js b/erpnext/selling/doctype/industry_type/industry_type.js
index 3878a79..3680906 100644
--- a/erpnext/selling/doctype/industry_type/industry_type.js
+++ b/erpnext/selling/doctype/industry_type/industry_type.js
@@ -1,13 +1,13 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-
+
//--------- ONLOAD -------------
cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
+
}
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
\ No newline at end of file
+
+}
diff --git a/erpnext/selling/doctype/industry_type/industry_type.py b/erpnext/selling/doctype/industry_type/industry_type.py
index 65b17e9..6d413ec 100644
--- a/erpnext/selling/doctype/industry_type/industry_type.py
+++ b/erpnext/selling/doctype/industry_type/industry_type.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class IndustryType(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/selling/doctype/industry_type/test_industry_type.py b/erpnext/selling/doctype/industry_type/test_industry_type.py
index 1246a24..d6cf79b 100644
--- a/erpnext/selling/doctype/industry_type/test_industry_type.py
+++ b/erpnext/selling/doctype/industry_type/test_industry_type.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Industry Type')
\ No newline at end of file
+
+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 ffcbb2d..128a941 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.py
+++ b/erpnext/selling/doctype/installation_note/installation_note.py
@@ -2,15 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
+from frappe import _
from frappe.utils import cstr, getdate
-from frappe import _
from erpnext.stock.utils import get_valid_serial_nos
-
from erpnext.utilities.transaction_base import TransactionBase
+
class InstallationNote(TransactionBase):
def __init__(self, *args, **kwargs):
super(InstallationNote, self).__init__(*args, **kwargs)
diff --git a/erpnext/selling/doctype/installation_note/test_installation_note.py b/erpnext/selling/doctype/installation_note/test_installation_note.py
index 553d070..abfda9c 100644
--- a/erpnext/selling/doctype/installation_note/test_installation_note.py
+++ b/erpnext/selling/doctype/installation_note/test_installation_note.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Installation Note')
diff --git a/erpnext/selling/doctype/installation_note_item/installation_note_item.py b/erpnext/selling/doctype/installation_note_item/installation_note_item.py
index 681b817..862c2a1 100644
--- a/erpnext/selling/doctype/installation_note_item/installation_note_item.py
+++ b/erpnext/selling/doctype/installation_note_item/installation_note_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class InstallationNoteItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/healthcare/doctype/organism_test_item/__init__.py b/erpnext/selling/doctype/party_specific_item/__init__.py
similarity index 100%
copy from erpnext/healthcare/doctype/organism_test_item/__init__.py
copy to erpnext/selling/doctype/party_specific_item/__init__.py
diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.js b/erpnext/selling/doctype/party_specific_item/party_specific_item.js
new file mode 100644
index 0000000..077b936
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Party Specific Item', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.json b/erpnext/selling/doctype/party_specific_item/party_specific_item.json
new file mode 100644
index 0000000..32b5d47
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.json
@@ -0,0 +1,77 @@
+{
+ "actions": [],
+ "creation": "2021-08-27 19:28:07.559978",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "party_type",
+ "party",
+ "column_break_3",
+ "restrict_based_on",
+ "based_on_value"
+ ],
+ "fields": [
+ {
+ "fieldname": "party_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Party Type",
+ "options": "Customer\nSupplier",
+ "reqd": 1
+ },
+ {
+ "fieldname": "party",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Party Name",
+ "options": "party_type",
+ "reqd": 1
+ },
+ {
+ "fieldname": "restrict_based_on",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Restrict Items Based On",
+ "options": "Item\nItem Group\nBrand",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "based_on_value",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Based On Value",
+ "options": "restrict_based_on",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-09-14 13:27:58.612334",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Party Specific Item",
+ "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",
+ "title_field": "party",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.py b/erpnext/selling/doctype/party_specific_item/party_specific_item.py
new file mode 100644
index 0000000..a408af5
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2021, 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 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,
+ })
+ 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
new file mode 100644
index 0000000..874a364
--- /dev/null
+++ b/erpnext/selling/doctype/party_specific_item/test_party_specific_item.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import unittest
+
+import frappe
+
+from erpnext.controllers.queries import item_query
+
+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.insert()
+
+class TestPartySpecificItem(unittest.TestCase):
+ def setUp(self):
+ self.customer = frappe.get_last_doc("Customer")
+ self.supplier = frappe.get_last_doc("Supplier")
+ 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)
+ 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)
+ 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 ae3482f..4c73916 100644
--- a/erpnext/selling/doctype/product_bundle/product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/product_bundle.py
@@ -2,13 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
+from frappe import _
+from frappe.model.document import Document
from frappe.utils import get_link_to_form
-from frappe import _
-
-from frappe.model.document import Document
class ProductBundle(Document):
def autoname(self):
diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.js b/erpnext/selling/doctype/product_bundle/test_product_bundle.js
index ba5ba0d..0dc90ec 100644
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.js
+++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.js
@@ -33,4 +33,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
index 7d1d372..13bd2a3 100644
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
@@ -3,8 +3,8 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
+
test_records = frappe.get_test_records('Product Bundle')
def make_product_bundle(parent, items, qty=None):
diff --git a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py
index 8721bfa..5f71a27 100644
--- a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py
+++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ProductBundleItem(Document):
pass
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 1223449..0e1a915 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -18,6 +18,8 @@
}
});
+ frm.set_df_property('packed_items', 'cannot_add_rows', true);
+ frm.set_df_property('packed_items', 'cannot_delete_rows', true);
},
refresh: function(frm) {
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 3eba62b..43a4490 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -43,6 +43,8 @@
"ignore_pricing_rule",
"items_section",
"items",
+ "bundle_items_section",
+ "packed_items",
"pricing_rule_details",
"pricing_rules",
"sec_break23",
@@ -926,6 +928,24 @@
"label": "Lost Reasons",
"options": "Quotation Lost Reason Detail",
"read_only": 1
+ },
+ {
+ "depends_on": "packed_items",
+ "fieldname": "packed_items",
+ "fieldtype": "Table",
+ "label": "Bundle Items",
+ "options": "Packed Item",
+ "print_hide": 1
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "packed_items",
+ "depends_on": "packed_items",
+ "fieldname": "bundle_items_section",
+ "fieldtype": "Section Break",
+ "label": "Bundle Items",
+ "options": "fa fa-suitcase",
+ "print_hide": 1
}
],
"icon": "fa fa-shopping-cart",
@@ -933,7 +953,7 @@
"is_submittable": 1,
"links": [],
"max_attachments": 1,
- "modified": "2020-10-30 13:58:59.212060",
+ "modified": "2021-08-27 20:10:07.864951",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index e4f8a47..99c43bf 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils import flt, nowdate, getdate
from frappe import _
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import flt, getdate, nowdate
from erpnext.controllers.selling_controller import SellingController
@@ -31,6 +32,9 @@
if self.items:
self.with_items = 1
+ from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
+ make_packing_list(self)
+
def validate_valid_till(self):
if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
frappe.throw(_("Valid till date cannot be before transaction date"))
diff --git a/erpnext/selling/doctype/quotation/quotation_dashboard.py b/erpnext/selling/doctype/quotation/quotation_dashboard.py
index f1ac951..9586cb1 100644
--- a/erpnext/selling/doctype/quotation/quotation_dashboard.py
+++ b/erpnext/selling/doctype/quotation/quotation_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'prevdoc_docname',
@@ -17,4 +19,4 @@
'items': ['Auto Repeat']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 527a999..a44089a 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt, add_days, nowdate, add_months, getdate
import unittest
+import frappe
+from frappe.utils import add_days, add_months, flt, getdate, nowdate
+
test_dependencies = ["Product Bundle"]
@@ -133,8 +134,10 @@
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 make_delivery_note, make_sales_invoice
+ from erpnext.selling.doctype.sales_order.sales_order import (
+ make_delivery_note,
+ make_sales_invoice,
+ )
rate_with_margin = flt((1500*18.75)/100 + 1500)
@@ -226,9 +229,87 @@
expired_quotation.reload()
self.assertEqual(expired_quotation.status, "Expired")
+ def test_product_bundle_mapping_on_creating_so(self):
+ from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+ from erpnext.selling.doctype.quotation.quotation import make_sales_order
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ make_item("_Test Product Bundle", {"is_stock_item": 0})
+ 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"])
+
+ 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]
+
+ 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]
+ ]
+ 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]
+ ]
+
+ self.assertEqual(quotation_packed_items, so_packed_items)
+
+ def test_product_bundle_price_calculation_when_calculate_bundle_price_is_unchecked(self):
+ from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ make_item("_Test Product Bundle", {"is_stock_item": 0})
+ 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"])
+
+ bundle_item1.valuation_rate = 100
+ bundle_item1.save()
+
+ bundle_item2.valuation_rate = 200
+ bundle_item2.save()
+
+ quotation = make_quotation(item_code="_Test Product Bundle", qty=2, rate=100)
+ self.assertEqual(quotation.items[0].amount, 200)
+
+ def test_product_bundle_price_calculation_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
+
+ make_item("_Test Product Bundle", {"is_stock_item": 0})
+ 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"])
+
+ enable_calculate_bundle_price()
+
+ quotation = make_quotation(item_code="_Test Product Bundle", qty=2, rate=100, do_not_submit=1)
+ quotation.packed_items[0].rate = 100
+ quotation.packed_items[1].rate = 200
+ quotation.save()
+
+ self.assertEqual(quotation.items[0].amount, 600)
+ self.assertEqual(quotation.items[0].rate, 300)
+
+ enable_calculate_bundle_price(enable=0)
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'
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js
index aeb5d1b..b59bb05 100644
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js
+++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js
@@ -41,4 +41,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js
index e7349e3..f5172fb 100644
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js
+++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js
@@ -35,4 +35,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js
index 5b4224d..0d34099 100644
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js
+++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js
@@ -33,4 +33,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js
index 50b8a83..84be56f 100644
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js
+++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js
@@ -36,4 +36,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js
index ac7ed65..5e21f81 100644
--- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js
+++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js
@@ -38,4 +38,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.py b/erpnext/selling/doctype/quotation_item/quotation_item.py
index 7384871..ea47249 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.py
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class QuotationItem(Document):
pass
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index b42c615..f692690 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -43,6 +43,9 @@
}
}
});
+
+ frm.set_df_property('packed_items', 'cannot_add_rows', true);
+ frm.set_df_property('packed_items', 'cannot_delete_rows', true);
},
refresh: function(frm) {
if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed'
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index d31db82..7c7ed9a 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -55,6 +55,8 @@
"items_section",
"scan_barcode",
"items",
+ "packing_list",
+ "packed_items",
"pricing_rule_details",
"pricing_rules",
"section_break_31",
@@ -101,8 +103,6 @@
"in_words",
"advance_paid",
"disable_rounded_total",
- "packing_list",
- "packed_items",
"payment_schedule_section",
"payment_terms_template",
"payment_schedule",
@@ -571,7 +571,8 @@
"fieldtype": "Data",
"hide_days": 1,
"hide_seconds": 1,
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -1018,6 +1019,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "packed_items",
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"hide_days": 1,
@@ -1028,14 +1030,14 @@
"print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"hide_days": 1,
"hide_seconds": 1,
"label": "Packed Items",
"options": "Packed Item",
- "print_hide": 1,
- "read_only": 1
+ "print_hide": 1
},
{
"fieldname": "payment_schedule_section",
@@ -1478,6 +1480,7 @@
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -1510,7 +1513,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2021-07-08 21:37:44.177493",
+ "modified": "2021-09-28 13:09:51.515542",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index bba5401..dcf478b 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -2,24 +2,32 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+
+import frappe
import frappe.utils
-from frappe.utils import cstr, flt, getdate, cint, nowdate, add_days, get_link_to_form, strip_html
from frappe import _
-from six import string_types
-from frappe.model.utils import get_fetch_values
-from frappe.model.mapper import get_mapped_doc
-from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
-from frappe.desk.notifications import clear_doctype_notifications
from frappe.contacts.doctype.address.address import get_company_address
+from frappe.desk.notifications import clear_doctype_notifications
+from frappe.model.mapper import get_mapped_doc
+from frappe.model.utils import get_fetch_values
+from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate, strip_html
+from six import string_types
+
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+ unlink_inter_company_doc,
+ update_linked_doc,
+ validate_inter_company_party,
+)
from erpnext.controllers.selling_controller import SellingController
+from erpnext.manufacturing.doctype.production_plan.production_plan import (
+ get_items_for_material_requests,
+)
from erpnext.selling.doctype.customer.customer import check_credit_limit
-from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
-from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
-from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
- unlink_inter_company_doc
+from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -102,7 +110,7 @@
if self.order_type == 'Sales' and not self.skip_delivery_note:
delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date]
max_delivery_date = max(delivery_date_list) if delivery_date_list else None
- if not self.delivery_date:
+ if (max_delivery_date and not self.delivery_date) or (max_delivery_date and getdate(self.delivery_date) != getdate(max_delivery_date)):
self.delivery_date = max_delivery_date
if self.delivery_date:
for d in self.get("items"):
@@ -111,8 +119,6 @@
if getdate(self.transaction_date) > getdate(d.delivery_date):
frappe.msgprint(_("Expected Delivery Date should be after Sales Order Date"),
indicator='orange', title=_('Warning'))
- if getdate(self.delivery_date) != getdate(max_delivery_date):
- self.delivery_date = max_delivery_date
else:
frappe.throw(_("Please enter Delivery Date"))
@@ -718,8 +724,7 @@
"doctype": "Maintenance Schedule Item",
"field_map": {
"parent": "sales_order"
- },
- "add_if_empty": True
+ }
}
}, target_doc)
@@ -745,8 +750,7 @@
"field_map": {
"parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype"
- },
- "add_if_empty": True
+ }
}
}, target_doc)
@@ -949,11 +953,52 @@
"pricing_rules"
],
"postprocess": update_item,
- "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map
+ "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": [
+ ["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"
+ ],
}
}, 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']
+ )
+
+ delivery_by_item = frappe._dict()
+ for date in delivery_dates:
+ delivery_by_item[date.item_code] = date.delivery_date
+
+ for item in items:
+ 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)
+
@frappe.whitelist()
def make_work_orders(items, sales_order, company, project=None):
'''Make Work Orders against the given Sales Order for the given `items`'''
diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
index 05a760d..ee3c707 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
+++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'sales_order',
@@ -41,4 +43,4 @@
'items': ['Payment Entry', 'Payment Request', 'Journal Entry']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.js b/erpnext/selling/doctype/sales_order/test_sales_order.js
deleted file mode 100644
index 57ed19b..0000000
--- a/erpnext/selling/doctype/sales_order/test_sales_order.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Order", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Sales Order', [
- // insert a new Sales Order
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index a0a21ee..bbfe7c0 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1,21 +1,29 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import json
import unittest
+
import frappe
import frappe.permissions
-from frappe.utils import flt, add_days, nowdate, getdate
from frappe.core.doctype.user_permission.test_user_permission import create_user
-from erpnext.selling.doctype.sales_order.sales_order \
- import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
-from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
+from frappe.utils import add_days, flt, getdate, nowdate
+
from erpnext.controllers.accounts_controller import update_child_qty_rate
-from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+from erpnext.selling.doctype.sales_order.sales_order import (
+ WarehouseRequired,
+ make_delivery_note,
+ make_material_request,
+ make_raw_material_request,
+ make_sales_invoice,
+ make_work_orders,
+)
from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
class TestSalesOrder(unittest.TestCase):
@@ -162,8 +170,8 @@
self.assertEqual(so.get("items")[0].delivered_qty, 9)
# Make return deliver note, sales invoice and check quantity
- from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
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
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3, do_not_submit=True)
dn1.items[0].against_sales_order = so.name
@@ -444,8 +452,8 @@
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
def test_update_child_with_precision(self):
- from frappe.model.meta import get_field_precision
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+ from frappe.model.meta import get_field_precision
precision = get_field_precision(frappe.get_meta("Sales Order Item").get_field("rate"))
@@ -731,9 +739,11 @@
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
def test_drop_shipping(self):
- from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_default_supplier, \
- update_status as so_update_status
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
+ from erpnext.selling.doctype.sales_order.sales_order import (
+ make_purchase_order_for_default_supplier,
+ )
+ 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})
@@ -815,8 +825,10 @@
so.cancel()
def test_drop_shipping_partial_order(self):
- from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_default_supplier, \
- update_status as so_update_status
+ from erpnext.selling.doctype.sales_order.sales_order import (
+ make_purchase_order_for_default_supplier,
+ )
+ 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})
@@ -869,7 +881,9 @@
def test_drop_shipping_full_for_default_suppliers(self):
"""Test if multiple POs are generated in one go against different default suppliers."""
- from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_default_supplier
+ from erpnext.selling.doctype.sales_order.sales_order import (
+ make_purchase_order_for_default_supplier,
+ )
if not frappe.db.exists("Item", "_Test Item for Drop Shipping 1"):
make_item("_Test Item for Drop Shipping 1", {"is_stock_item": 1, "delivered_by_supplier": 1})
@@ -906,6 +920,38 @@
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.
+ """
+ from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order
+
+ product_bundle = make_item("_Test Product Bundle", {"is_stock_item": 0})
+ 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"])
+
+ so_items = [
+ {
+ "item_code": product_bundle.item_code,
+ "warehouse": "",
+ "qty": 2,
+ "rate": 400,
+ "delivered_by_supplier": 1,
+ "supplier": '_Test Supplier'
+ }
+ ]
+
+ so = make_sales_order(item_list=so_items)
+
+ purchase_order = make_purchase_order(so.name, selected_items=so_items)
+
+ self.assertEqual(purchase_order.items[0].item_code, "_Test Bundle Item 1")
+ self.assertEqual(purchase_order.items[1].item_code, "_Test Bundle Item 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"])
@@ -1043,8 +1089,7 @@
}]
})
so.submit()
- from erpnext.manufacturing.doctype.work_order.test_work_order import \
- make_wo_order_test_record
+ 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.fg_warehouse = "_Test Warehouse - _TC"
@@ -1052,8 +1097,9 @@
work_order.submit()
make_stock_entry(item_code=item.item_code, target="_Test Warehouse - _TC", qty=1)
item_serial_no = frappe.get_doc("Serial No", {"item_code": item.item_code})
- from erpnext.manufacturing.doctype.work_order.work_order import \
- make_stock_entry as make_production_stock_entry
+ 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
@@ -1085,8 +1131,9 @@
si = make_sales_invoice(so.name)
si.update_stock = 0
si.submit()
- from erpnext.accounts.doctype.sales_invoice.sales_invoice import \
- make_delivery_note as make_delivery_note_from_invoice
+ 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()
@@ -1123,6 +1170,7 @@
def test_cancel_sales_order_after_cancel_payment_entry(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+
# make a sales order
so = make_sales_order()
@@ -1222,7 +1270,7 @@
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
+ Expected result: sales order should not get cancelled
"""
so = make_sales_order()
so.submit()
@@ -1232,7 +1280,9 @@
self.assertRaises(frappe.ValidationError, so.cancel)
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
- from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
+ create_payment_terms_template,
+ )
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
automatically_fetch_payment_terms()
@@ -1250,7 +1300,7 @@
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
compare_payment_schedules(self, so, si)
- automatically_fetch_payment_terms(enable=0)
+ automatically_fetch_payment_terms(enable=0)
def automatically_fetch_payment_terms(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js
index 7426868..9eebfda 100644
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js
+++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js
@@ -35,4 +35,3 @@
() => done()
]);
});
-
diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js
index 8e05385..be76c49 100644
--- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js
+++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js
@@ -56,4 +56,4 @@
},
() => done()
]);
-});
\ No newline at end of file
+});
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 27f303d..772aa6c 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py
@@ -2,12 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe.model.document import Document
+
class SalesOrderItem(Document):
pass
def on_doctype_update():
- frappe.db.add_index("Sales Order Item", ["item_code", "warehouse"])
\ No newline at end of file
+ frappe.db.add_index("Sales Order Item", ["item_code", "warehouse"])
diff --git a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.py b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.py
index 68d289f..bdabef2 100644
--- a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.py
+++ b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SalesPartnerType(Document):
pass
diff --git a/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.js b/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.js
deleted file mode 100644
index 3ed7b46..0000000
--- a/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Sales Partner Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Sales Partner Type
- () => frappe.tests.make('Sales Partner Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.py b/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.py
index fb8f8b0..895b0ec 100644
--- a/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.py
+++ b/erpnext/selling/doctype/sales_partner_type/test_sales_partner_type.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSalesPartnerType(unittest.TestCase):
pass
diff --git a/erpnext/selling/doctype/sales_team/sales_team.py b/erpnext/selling/doctype/sales_team/sales_team.py
index 1832108..9b542c0 100644
--- a/erpnext/selling/doctype/sales_team/sales_team.py
+++ b/erpnext/selling/doctype/sales_team/sales_team.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class SalesTeam(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js
index 95a4243..cf6fb28 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.js
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.js
@@ -6,26 +6,3 @@
}
});
-
-frappe.tour['Selling Settings'] = [
- {
- fieldname: "cust_master_name",
- title: "Customer Naming By",
- description: __("By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
- },
- {
- fieldname: "selling_price_list",
- title: "Default Selling Price List",
- description: __("Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.")
- },
- {
- fieldname: "so_required",
- title: "Sales Order Required for Sales Invoice & Delivery Note Creation",
- description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.")
- },
- {
- fieldname: "dn_required",
- title: "Delivery Note Required for Sales Invoice Creation",
- description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.")
- }
-];
\ No newline at end of file
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 717fd9b..c27f1ea 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -18,17 +18,18 @@
"close_opportunity_after_days",
"item_price_settings_section",
"selling_price_list",
+ "maintain_same_rate_action",
+ "role_to_override_stop_action",
"column_break_15",
"maintain_same_sales_rate",
- "maintain_same_rate_action",
"editable_price_list_rate",
"validate_selling_price",
+ "editable_bundle_item_rates",
"sales_transactions_settings_section",
"so_required",
"dn_required",
"sales_update_frequency",
"column_break_5",
- "role_to_override_stop_action",
"allow_multiple_items",
"allow_against_multiple_purchase_orders",
"hide_tax_id"
@@ -40,14 +41,14 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Customer Naming By",
- "options": "Customer Name\nNaming Series"
+ "options": "Customer Name\nNaming Series\nAuto Name"
},
{
"fieldname": "campaign_naming_by",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Campaign Naming By",
- "options": "Campaign Name\nNaming Series"
+ "options": "Campaign Name\nNaming Series\nAuto Name"
},
{
"fieldname": "customer_group",
@@ -146,15 +147,14 @@
{
"default": "Stop",
"depends_on": "maintain_same_sales_rate",
- "description": "Configure the action to stop the transaction or just warn if the same rate is not maintained.",
"fieldname": "maintain_same_rate_action",
"fieldtype": "Select",
- "label": "Action if Same Rate is Not Maintained",
+ "label": "Action if Same Rate is Not Maintained Throughout Sales Cycle",
"mandatory_depends_on": "maintain_same_sales_rate",
"options": "Stop\nWarn"
},
{
- "depends_on": "eval: doc.maintain_same_rate_action == 'Stop'",
+ "depends_on": "eval: doc.maintain_same_sales_rate && doc.maintain_same_rate_action == 'Stop'",
"fieldname": "role_to_override_stop_action",
"fieldtype": "Link",
"label": "Role Allowed to Override Stop Action",
@@ -191,6 +191,12 @@
"fieldname": "sales_transactions_settings_section",
"fieldtype": "Section Break",
"label": "Transaction Settings"
+ },
+ {
+ "default": "0",
+ "fieldname": "editable_bundle_item_rates",
+ "fieldtype": "Check",
+ "label": "Calculate Product Bundle Price based on Child Items' Rates"
}
],
"icon": "fa fa-cog",
@@ -198,7 +204,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-08-06 22:25:50.119458",
+ "modified": "2021-09-08 19:38:10.175989",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py
index b219e7e..5bed43e 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.py
@@ -4,17 +4,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import frappe.defaults
-from frappe.utils import cint
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+from frappe.model.document import Document
+from frappe.utils import cint
from frappe.utils.nestedset import get_root_of
-from frappe.model.document import Document
class SellingSettings(Document):
def on_update(self):
self.toggle_hide_tax_id()
+ self.toggle_editable_rate_for_bundle_items()
def validate(self):
for key in ["cust_master_name", "campaign_naming_by", "customer_group", "territory",
@@ -33,6 +34,11 @@
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)
+
def set_default_customer_group_and_territory(self):
if not self.customer_group:
self.customer_group = get_root_of('Customer Group')
diff --git a/erpnext/selling/doctype/selling_settings/test_selling_settings.py b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
index 961a54d..572a110 100644
--- a/erpnext/selling/doctype/selling_settings/test_selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/test_selling_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestSellingSettings(unittest.TestCase):
pass
diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py
index d142d16..45aee4e 100644
--- a/erpnext/selling/doctype/sms_center/sms_center.py
+++ b/erpnext/selling/doctype/sms_center/sms_center.py
@@ -2,14 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-
-from frappe.utils import cstr
-from frappe import msgprint, _
-
-from frappe.model.document import Document
-
+from frappe import _, msgprint
from frappe.core.doctype.sms_settings.sms_settings import send_sms
+from frappe.model.document import Document
+from frappe.utils import cstr
+
class SMSCenter(Document):
@frappe.whitelist()
@@ -83,4 +82,3 @@
receiver_list = self.get_receiver_nos()
if receiver_list:
send_sms(receiver_list, cstr(self.message))
-
diff --git a/erpnext/selling/form_tour/sales_order/sales_order.json b/erpnext/selling/form_tour/sales_order/sales_order.json
new file mode 100644
index 0000000..a81eb4a
--- /dev/null
+++ b/erpnext/selling/form_tour/sales_order/sales_order.json
@@ -0,0 +1,97 @@
+{
+ "creation": "2021-06-29 21:13:36.089054",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 21:13:36.089054",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order",
+ "owner": "Administrator",
+ "reference_doctype": "Sales Order",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "Select a customer.",
+ "field": "",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Customer",
+ "next_step_condition": "customer",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Select Customer"
+ },
+ {
+ "description": "You can add items here.",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Items",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "List of items"
+ },
+ {
+ "child_doctype": "Sales Order Item",
+ "description": "Select an item.",
+ "field": "",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 1,
+ "label": "Item Code",
+ "next_step_condition": "eval: doc.item_code",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Select Item"
+ },
+ {
+ "child_doctype": "Sales Order Item",
+ "description": "Enter quantity.",
+ "field": "",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Quantity",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Enter Quantity"
+ },
+ {
+ "child_doctype": "Sales Order Item",
+ "description": "Enter rate of the item.",
+ "field": "",
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "has_next_condition": 0,
+ "is_table_field": 1,
+ "label": "Rate",
+ "parent_field": "",
+ "parent_fieldname": "items",
+ "position": "Right",
+ "title": "Enter Rate"
+ },
+ {
+ "description": "You can add sales taxes and charges here.",
+ "field": "",
+ "fieldname": "taxes",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Sales Taxes and Charges",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Add Sales Taxes and Charges"
+ }
+ ],
+ "title": "Sales Order"
+}
\ No newline at end of file
diff --git a/erpnext/selling/form_tour/selling_settings/selling_settings.json b/erpnext/selling/form_tour/selling_settings/selling_settings.json
new file mode 100644
index 0000000..20c718f
--- /dev/null
+++ b/erpnext/selling/form_tour/selling_settings/selling_settings.json
@@ -0,0 +1,65 @@
+{
+ "creation": "2021-06-29 20:39:19.408763",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-06-29 20:49:01.359489",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Selling Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Selling Settings",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a <a href=\"https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series\" target=\"_blank\">Naming Series</a>. Choose the 'Naming Series' option.",
+ "field": "",
+ "fieldname": "cust_master_name",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Customer Naming By",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Customer Naming By"
+ },
+ {
+ "description": "If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.",
+ "field": "",
+ "fieldname": "so_required",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Is Sales Order Required for Sales Invoice & Delivery Note Creation?",
+ "parent_field": "",
+ "position": "Left",
+ "title": "Sales Order Required for Sales Invoice & Delivery Note Creation"
+ },
+ {
+ "description": "If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.",
+ "field": "",
+ "fieldname": "dn_required",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Is Delivery Note Required for Sales Invoice Creation?",
+ "parent_field": "",
+ "position": "Left",
+ "title": "Delivery Note Required for Sales Invoice Creation"
+ },
+ {
+ "description": "Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.",
+ "field": "",
+ "fieldname": "selling_price_list",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Default Price List",
+ "parent_field": "",
+ "position": "Right",
+ "title": "Default Selling Price List"
+ }
+ ],
+ "title": "Selling Settings"
+}
\ No newline at end of file
diff --git a/erpnext/selling/module_onboarding/selling/selling.json b/erpnext/selling/module_onboarding/selling/selling.json
index 160208f..c02f444 100644
--- a/erpnext/selling/module_onboarding/selling/selling.json
+++ b/erpnext/selling/module_onboarding/selling/selling.json
@@ -19,32 +19,17 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/selling",
"idx": 0,
"is_complete": 0,
- "modified": "2020-07-08 14:05:37.669753",
+ "modified": "2021-08-24 18:12:38.052434",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
"owner": "Administrator",
"steps": [
{
- "step": "Introduction to Selling"
- },
- {
- "step": "Create a Customer"
- },
- {
- "step": "Setup your Warehouse"
- },
- {
- "step": "Create a Product"
- },
- {
- "step": "Create a Quotation"
- },
- {
- "step": "Create your first Sales Order"
- },
- {
"step": "Selling Settings"
+ },
+ {
+ "step": "Sales Order"
}
],
"subtitle": "Products, Sales, Analysis, and more.",
diff --git a/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json
index 5a403b0..64defbf 100644
--- a/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json
+++ b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json
@@ -5,7 +5,6 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:16:19.731719",
@@ -13,6 +12,7 @@
"name": "Create a Customer",
"owner": "Administrator",
"reference_document": "Customer",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Customer",
"validate_action": 1
diff --git a/erpnext/selling/onboarding_step/create_a_product/create_a_product.json b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json
index d2068e1..52a5861 100644
--- a/erpnext/selling/onboarding_step/create_a_product/create_a_product.json
+++ b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json
@@ -5,14 +5,14 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-05-12 18:30:02.489949",
+ "modified": "2020-10-14 14:53:00.133574",
"modified_by": "Administrator",
"name": "Create a Product",
"owner": "Administrator",
"reference_document": "Item",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Product",
"validate_action": 1
diff --git a/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json
index 27253d1..6f1837e 100644
--- a/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json
+++ b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json
@@ -5,7 +5,6 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:34:58.958641",
@@ -13,6 +12,7 @@
"name": "Create a Quotation",
"owner": "Administrator",
"reference_document": "Quotation",
+ "show_form_tour": 0,
"show_full_form": 1,
"title": "Create a Quotation",
"validate_action": 1
diff --git a/erpnext/selling/onboarding_step/create_a_sales_order/create_a_sales_order.json b/erpnext/selling/onboarding_step/create_a_sales_order/create_a_sales_order.json
new file mode 100644
index 0000000..378f295
--- /dev/null
+++ b/erpnext/selling/onboarding_step/create_a_sales_order/create_a_sales_order.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Create Sales Order",
+ "creation": "2021-06-29 21:22:54.204880",
+ "description": "A Sales Order is a confirmation of an order from your customer. It is also referred to as Proforma Invoice.\n\nSales Order at the heart of your sales and purchase transactions. Sales Orders are linked in Delivery Note, Sales Invoices, Material Request, and Maintenance transactions. Through Sales Order, you can track fulfillment of the overall deal towards the customer.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-06-29 21:22:54.204880",
+ "modified_by": "Administrator",
+ "name": "Create a Sales Order",
+ "owner": "Administrator",
+ "reference_document": "Sales Order",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Create a Sales Order",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json
index d21c1f4..6364533 100644
--- a/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json
+++ b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json
@@ -5,13 +5,13 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:29:13.703177",
"modified_by": "Administrator",
"name": "Introduction to Selling",
"owner": "Administrator",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Introduction to Selling",
"validate_action": 1,
diff --git a/erpnext/selling/onboarding_step/sales_order/sales_order.json b/erpnext/selling/onboarding_step/sales_order/sales_order.json
new file mode 100644
index 0000000..d616fae
--- /dev/null
+++ b/erpnext/selling/onboarding_step/sales_order/sales_order.json
@@ -0,0 +1,21 @@
+{
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s convert your first Sales Order against a Quotation",
+ "creation": "2021-08-13 14:03:49.217866",
+ "description": "# Sales Order\n\nA Sales Order is a confirmation of an order from your customer. It is also referred to as Proforma Invoice.\n\nSales Order at the heart of your sales and purchase transactions. Sales Orders are linked in Delivery Note, Sales Invoices, Material Request, and Maintenance transactions. Through Sales Order, you can track fulfillment of the overall deal towards the customer.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-24 18:12:32.394992",
+ "modified_by": "Administrator",
+ "name": "Sales Order",
+ "owner": "Administrator",
+ "reference_document": "Sales Order",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Sales Order",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/selling/onboarding_step/selling_settings/selling_settings.json b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json
index 7996d7b..ec30fd3 100644
--- a/erpnext/selling/onboarding_step/selling_settings/selling_settings.json
+++ b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json
@@ -1,19 +1,21 @@
{
"action": "Show Form Tour",
+ "action_label": "Let\u2019s walk-through Selling Settings",
"creation": "2020-06-01 13:01:45.615189",
+ "description": "# Selling Settings\n\nCRM and Selling module\u2019s features are configurable as per your business needs. Selling Settings is the place where you can set your preferences for:\n - Customer naming and default values\n - Billing and shipping preference in sales transactions\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
- "modified": "2020-06-01 13:04:14.980743",
+ "modified": "2021-08-24 18:11:21.264335",
"modified_by": "Administrator",
"name": "Selling Settings",
"owner": "Administrator",
"reference_document": "Selling Settings",
+ "show_form_tour": 0,
"show_full_form": 0,
- "title": "Configure Selling Settings.",
+ "title": "Selling Settings",
"validate_action": 0
}
\ No newline at end of file
diff --git a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
index 9457dee..1e20eb0 100644
--- a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
+++ b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
@@ -5,15 +5,15 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-07-04 12:33:16.970031",
+ "modified": "2020-10-14 14:53:25.538900",
"modified_by": "Administrator",
"name": "Setup your Warehouse",
"owner": "Administrator",
"path": "Tree/Warehouse",
"reference_document": "Warehouse",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Set up your Warehouse",
"validate_action": 1
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 8d1f112..b4338c9 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -2,11 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, json
+
+import json
+
+import frappe
from frappe.utils.nestedset import get_root_of
-from frappe.utils import cint
-from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
+
from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability
+from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
+
def search_by_term(search_term, warehouse, price_list):
result = search_for_serial_or_batch_or_barcode_number(search_term) or {}
@@ -146,7 +150,7 @@
if not item['is_stock_item']:
if not frappe.db.exists('Product Bundle', item['item_code']):
items.remove(item)
-
+
return items
def get_conditions(search_term):
@@ -209,7 +213,6 @@
@frappe.whitelist()
def create_opening_voucher(pos_profile, company, balance_details):
- import json
balance_details = json.loads(balance_details)
new_pos_opening = frappe.get_doc({
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index c827368..e61a634 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -525,7 +525,7 @@
}
} else {
- if (!this.frm.doc.customer)
+ if (!this.frm.doc.customer)
return this.raise_customer_selection_alert();
const { item_code, batch_no, serial_no, rate } = item;
@@ -549,7 +549,7 @@
await this.check_stock_availability(item_row, value, this.frm.doc.set_warehouse);
await this.trigger_new_item_events(item_row);
-
+
this.update_cart_html(item_row);
if (this.item_details.$component.is(':visible'))
@@ -708,4 +708,3 @@
.catch(e => console.log(e));
}
};
-
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 a4a4b0e..9d8338e 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -973,7 +973,7 @@
load_invoice() {
const frm = this.events.get_frm();
-
+
this.attach_refresh_field_event(frm);
this.fetch_customer_details(frm.doc.customer).then(() => {
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 6a4d3d5..ec861d7 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -65,7 +65,7 @@
// if item is null or highlighted cart item is clicked twice
const hide_item_details = !Boolean(item) || !current_item_changed;
-
+
this.events.toggle_item_selector(!hide_item_details);
this.toggle_component(!hide_item_details);
@@ -127,7 +127,7 @@
this.$item_price.html(format_currency(price_list_rate, this.currency));
if (!this.hide_images && image) {
this.$item_image.html(
- `<img
+ `<img
onerror="cur_pos.item_details.handle_broken_image(this)"
class="h-full" src="${image}"
alt="${frappe.get_abbr(item_name)}"
@@ -236,7 +236,7 @@
if (this.value) {
me.events.form_updated(me.current_item, 'warehouse', this.value).then(() => {
me.item_stock_map = me.events.get_item_stock_map();
- const available_qty = me.item_stock_map[me.item_row.item_code][this.value];
+ const available_qty = me.item_stock_map[me.item_row.item_code] && me.item_stock_map[me.item_row.item_code][this.value];
if (available_qty === undefined) {
me.events.get_available_stock(me.item_row.item_code, this.value).then(() => {
// item stock map is updated now reset warehouse
@@ -416,4 +416,4 @@
toggle_component(show) {
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
}
-}
\ No newline at end of file
+}
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 dd7f143..8352b14 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -95,7 +95,7 @@
<span class="indicator-pill whitespace-nowrap ${indicator_color}">${qty_to_display}</span>
</div>
<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
- <img
+ <img
onerror="cur_pos.item_selector.handle_broken_image(this)"
class="h-full" src="${item_image}"
alt="${frappe.get_abbr(item.item_name)}"
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 962bcaf..95293d1 100644
--- a/erpnext/selling/page/point_of_sale/pos_number_pad.js
+++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js
@@ -44,4 +44,4 @@
me.events.numpad_event($btn);
});
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js
index 70c7dc2..e0993e2 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js
@@ -120,4 +120,4 @@
toggle_component(show) {
show ? this.$component.css('display', 'flex') && this.refresh_list() : this.$component.css('display', 'none');
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
index cec831d..dd9e05a 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
@@ -402,4 +402,4 @@
toggle_component(show) {
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 63306ad..7ddbf45 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -253,41 +253,6 @@
}
}
- setup_listener_for_payments() {
- frappe.realtime.on("process_phone_payment", (data) => {
- const doc = this.events.get_frm().doc;
- const { response, amount, success, failure_message } = data;
- let message, title;
-
- if (success) {
- title = __("Payment Received");
- if (amount >= doc.grand_total) {
- frappe.dom.unfreeze();
- message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]);
- this.events.submit_invoice();
- cur_frm.reload_doc();
-
- } else {
- message = __("Payment of {0} received successfully. Waiting for other requests to complete...", [format_currency(amount, doc.currency, 0)]);
- }
- } else if (failure_message) {
- message = failure_message;
- title = __("Payment Failed");
- }
-
- frappe.msgprint({ "message": message, "title": title });
- });
- }
-
- auto_set_remaining_amount() {
- const doc = this.events.get_frm().doc;
- const remaining_amount = doc.grand_total - doc.paid_amount;
- const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined;
- if (!current_value && remaining_amount > 0 && this.selected_mode) {
- this.selected_mode.set_value(remaining_amount);
- }
- }
-
attach_shortcuts() {
const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`);
@@ -332,6 +297,7 @@
this.render_payment_mode_dom();
this.make_invoice_fields_control();
this.update_totals_section();
+ this.focus_on_default_mop();
}
edit_cart() {
@@ -413,17 +379,24 @@
});
this[`${mode}_control`].toggle_label(false);
this[`${mode}_control`].set_value(p.amount);
+ });
+ this.render_loyalty_points_payment_mode();
+
+ this.attach_cash_shortcuts(doc);
+ }
+
+ focus_on_default_mop() {
+ const doc = this.events.get_frm().doc;
+ const payments = doc.payments;
+ payments.forEach(p => {
+ const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase();
if (p.default) {
setTimeout(() => {
this.$payment_modes.find(`.${mode}.mode-of-payment-control`).parent().click();
}, 500);
}
});
-
- this.render_loyalty_points_payment_mode();
-
- this.attach_cash_shortcuts(doc);
}
attach_cash_shortcuts(doc) {
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.css b/erpnext/selling/page/sales_funnel/sales_funnel.css
index 455d37c..60b2392 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.css
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.css
@@ -1,4 +1,4 @@
.funnel-wrapper {
margin: 15px;
width: 100%;
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py
index b613718..043a3e7 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.py
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
-from erpnext.accounts.report.utils import convert
+import frappe
import pandas as pd
+from frappe import _
+
+from erpnext.accounts.report.utils import convert
+
def validate_filters(from_date, to_date, company):
if from_date and to_date and (from_date >= to_date):
@@ -32,7 +34,7 @@
and (opportunity!="" or quotation_to="Lead") and company=%s""", (from_date, to_date, company))[0][0]
converted = frappe.db.sql("""select count(*) from `tabCustomer`
- JOIN `tabLead` ON `tabLead`.name = `tabCustomer`.lead_name
+ 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]
@@ -97,4 +99,4 @@
return result
else:
- return 'empty'
\ No newline at end of file
+ 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 a9e4303..fea19f9 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
@@ -2,10 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from six.moves import range
-from six import iteritems
-import frappe
+import frappe
+from six import iteritems
+from six.moves import range
field_map = {
"Contact": [ "first_name", "last_name", "phone", "mobile_no", "email_id", "is_primary_contact" ],
@@ -117,4 +117,4 @@
"Sales Partner": "partner_type"
}
- return group[party_type]
\ No newline at end of file
+ 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 056492a..c7040be 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
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils import flt
+
def execute(filters=None):
if not filters: filters = {}
@@ -76,4 +78,4 @@
last_sbom = line.get("parent")
actual_dict = sbom_map.setdefault(last_sbom, {})
actual_dict.setdefault(line.get("warehouse"), line.get("qty"))
- return sbom_map
\ No newline at end of file
+ return sbom_map
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
index d93ffb7..1b931e1 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
@@ -41,4 +41,4 @@
}
return value;
}
-}
\ No newline at end of file
+}
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 f15f63d..a29b5c8 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
@@ -2,11 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import calendar
+
import frappe
from frappe import _
from frappe.utils import cint, cstr, getdate
+
def execute(filters=None):
common_columns = [
{
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 6fb7666..ed2fbfd 100644
--- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
+++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
-from erpnext.selling.doctype.customer.customer import get_customer_outstanding, get_credit_limit
+
+from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
+
def execute(filters=None):
if not filters: filters = {}
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 eb9273a..535d551 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
@@ -4,10 +4,11 @@
from __future__ import unicode_literals
import frappe
+from frappe import _
+
from erpnext import get_default_company
from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_price_list_rate_for
-from frappe import _
def execute(filters=None):
diff --git a/erpnext/selling/report/inactive_customers/inactive_customers.py b/erpnext/selling/report/inactive_customers/inactive_customers.py
index e7aff36..c79efe2 100644
--- a/erpnext/selling/report/inactive_customers/inactive_customers.py
+++ b/erpnext/selling/report/inactive_customers/inactive_customers.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cint
from frappe import _
+from frappe.utils import cint
+
def execute(filters=None):
if not filters: filters ={}
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
index f47d67f..073be78 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
@@ -60,4 +60,4 @@
}
return value;
}
-};
\ No newline at end of file
+};
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 8473276..5396312 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
@@ -2,11 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils.nestedset import get_descendants_of
+
def execute(filters=None):
filters = frappe._dict(filters or {})
if filters.from_date > filters.to_date:
@@ -266,4 +268,4 @@
]
},
"type" : "bar"
- }
\ No newline at end of file
+ }
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 e89c451..f241a3e 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
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
columns = get_columns()
data = get_data()
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 f2518f0..95e332a 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
@@ -2,12 +2,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import unittest
-from frappe.utils import nowdate, add_months
-from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request\
- import execute
+
+from frappe.utils import add_months, nowdate
+
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import (
+ execute,
+)
class TestPendingSOItemsForPurchaseRequest(unittest.TestCase):
diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.js b/erpnext/selling/report/quotation_trends/quotation_trends.js
index f00ca27..97a1931 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.js
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.js
@@ -6,4 +6,3 @@
filters: erpnext.get_sales_trends_filters()
}
});
-
diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.py b/erpnext/selling/report/quotation_trends/quotation_trends.py
index 968e2ff..d2ee9a8 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.py
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
-from erpnext.controllers.trends import get_columns, get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
index 9089b53..6b03c7d 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -141,5 +141,3 @@
});
},
}
-
-
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index d036a1c..56bcb31 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, scrub
-from frappe.utils import getdate, flt, add_to_date, add_days
+from frappe.utils import add_days, add_to_date, flt, getdate
from six import iteritems
+
from erpnext.accounts.utils import get_fiscal_year
+
def execute(filters=None):
return Analytics(filters).run()
@@ -293,7 +296,7 @@
return period
def get_period_date_ranges(self):
- from dateutil.relativedelta import relativedelta, MO
+ from dateutil.relativedelta import MO, relativedelta
from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
increment = {
diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py
index 4d81a1e..a180099 100644
--- a/erpnext/selling/report/sales_analytics/test_analytics.py
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -2,11 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-import frappe.defaults
+
import unittest
-from erpnext.selling.report.sales_analytics.sales_analytics import execute
+
+import frappe
+
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.selling.report.sales_analytics.sales_analytics import execute
+
class TestAnalytics(unittest.TestCase):
def test_sales_analytics(self):
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 8cb2446..5c4d8b6 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import copy
+
+import frappe
from frappe import _
-from frappe.utils import flt, date_diff, getdate
+from frappe.utils import date_diff, flt, getdate
+
def execute(filters=None):
if not filters:
@@ -70,7 +73,7 @@
`tabSales Order` so,
`tabSales Order Item` soi
LEFT JOIN `tabSales Invoice Item` sii
- ON sii.so_detail = soi.name
+ ON sii.so_detail = soi.name and sii.docstatus = 1
WHERE
soi.parent = so.name
and so.status not in ('Stopped', 'Closed', 'On Hold')
@@ -276,4 +279,4 @@
})
- return columns
\ No newline at end of file
+ return columns
diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.js b/erpnext/selling/report/sales_order_trends/sales_order_trends.js
index ea320d6..b22ea8f 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.js
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.js
@@ -5,4 +5,4 @@
frappe.query_reports["Sales Order Trends"] = {
filters: erpnext.get_sales_trends_filters()
}
-});
\ No newline at end of file
+});
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 de7d3f2..89daf44 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.py
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
-from erpnext.controllers.trends import get_columns,get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
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 66f9aae..a84dec0 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
@@ -2,9 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
def execute(filters=None):
@@ -110,4 +110,4 @@
if filters.get("to_date"):
conditions += " and {0} <= %(to_date)s".format(date_field)
- return conditions
\ No newline at end of file
+ return conditions
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 ae216ca..d4f49c71 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
@@ -2,12 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt
-from erpnext.accounts.utils import get_fiscal_year
+
+from erpnext.accounts.doctype.monthly_distribution.monthly_distribution import (
+ get_periodwise_distribution_data,
+)
from erpnext.accounts.report.financial_statements import get_period_list
-from erpnext.accounts.doctype.monthly_distribution.monthly_distribution import get_periodwise_distribution_data
+from erpnext.accounts.utils import get_fiscal_year
+
def get_data_column(filters, partner_doctype):
data = []
@@ -208,4 +212,4 @@
return frappe.get_all('Target Detail',
filters = filters_dict,
- fields = ["parent", "item_group", target_qty_amt_field, "fiscal_year", "distribution_id"])
\ No newline at end of file
+ fields = ["parent", "item_group", target_qty_amt_field, "fiscal_year", "distribution_id"])
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
index 38bb127..adae47b 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
@@ -47,9 +47,9 @@
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
-
+
if (column.fieldname.includes('variance')) {
-
+
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py
index e41011f..de21c4a 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import get_data_column
+
+from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import (
+ get_data_column,
+)
+
def execute(filters=None):
data = []
return get_data_column(filters, "Sales Partner")
-
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 5356028..39ec072 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
@@ -2,9 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
def execute(filters=None):
@@ -165,4 +165,4 @@
`tabItem Group` where lft >= %s and rgt <= %s)""" % (lft, rgt)
- return conditions
\ No newline at end of file
+ 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 0c84909..13245b7 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
@@ -2,9 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
def execute(filters=None):
@@ -101,7 +101,7 @@
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("""
select
@@ -111,7 +111,7 @@
`tab%s` dt, `tabSales Team` st
where
st.parent = dt.name and st.parenttype = %s
- and dt.docstatus = 1 %s order by dt.name desc,st.sales_person
+ 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)
@@ -138,5 +138,3 @@
values.append(filters["to_date"])
return " and ".join(conditions), values
-
-
diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
index a8e2fad..2b84436 100644
--- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
@@ -47,9 +47,9 @@
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
-
+
if (column.fieldname.includes('variance')) {
-
+
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py
index 5166cc8..83a1c2c 100644
--- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py
+++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py
@@ -2,10 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import get_data_column
+
+from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import (
+ get_data_column,
+)
+
def execute(filters=None):
data = []
- return get_data_column(filters, "Sales Person")
\ No newline at end of file
+ return get_data_column(filters, "Sales Person")
diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js
index b236151..e269f02 100644
--- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js
+++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js
@@ -67,4 +67,4 @@
default: 0,
},
]
-}
\ No newline at end of file
+}
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 41c7f76..9dc2923 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
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
-from frappe.utils import flt
+from frappe import _, msgprint
+
from erpnext import get_company_currency
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
index 263391a..9f3d255 100644
--- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
@@ -47,9 +47,9 @@
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
-
+
if (column.fieldname.includes('variance')) {
-
+
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.py b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.py
index b1d89cc..b340124 100644
--- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.py
+++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.py
@@ -2,8 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import get_data_column
+
+from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import (
+ get_data_column,
+)
+
def execute(filters=None):
data = []
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 e883500..c334381 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
@@ -2,10 +2,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from erpnext import get_default_currency
from frappe import _
+from erpnext import get_default_currency
+
+
def execute(filters=None):
filters = frappe._dict(filters)
columns = get_columns()
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index f515baf..ddd4c4e 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -26,7 +26,7 @@
}
};
});
- }
+ }
setup_queries() {
var me = this;
@@ -63,7 +63,7 @@
this.frm.set_query("item_code", "items", function() {
return {
query: "erpnext.controllers.queries.item_query",
- filters: {'is_sales_item': 1}
+ filters: {'is_sales_item': 1, 'customer': cur_frm.doc.customer}
}
});
}
@@ -85,15 +85,12 @@
refresh() {
super.refresh();
-
+
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
this.frm.toggle_display("customer_name",
(this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer));
- if(this.frm.fields_dict.packed_items) {
- var packing_list_exists = (this.frm.doc.packed_items || []).length;
- this.frm.toggle_display("packing_list", packing_list_exists ? true : false);
- }
+
this.toggle_editable_price_list_rate();
}
@@ -250,7 +247,12 @@
var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate"));
if(df && editable_price_list_rate) {
- df.read_only = 0;
+ const parent_field = frappe.meta.get_parentfield(this.frm.doc.doctype, this.frm.doc.doctype + " Item");
+ if (!this.frm.fields_dict[parent_field]) return;
+
+ this.frm.fields_dict[parent_field].grid.update_docfield_property(
+ 'price_list_rate', 'read_only', 0
+ );
}
}
@@ -394,6 +396,10 @@
}
_set_batch_number(doc) {
+ if (doc.batch_no) {
+ return
+ }
+
let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)};
if (doc.has_serial_no && doc.serial_no) {
args['serial_no'] = doc.serial_no
diff --git a/erpnext/setup/default_energy_point_rules.py b/erpnext/setup/default_energy_point_rules.py
index 94f5aa4..c41d000 100644
--- a/erpnext/setup/default_energy_point_rules.py
+++ b/erpnext/setup/default_energy_point_rules.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
from frappe import _
doctype_rule_map = {
@@ -55,4 +56,3 @@
'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 b8b09cb..be072fc 100644
--- a/erpnext/setup/default_success_action.py
+++ b/erpnext/setup/default_success_action.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
from frappe import _
doctype_list = [
@@ -24,4 +25,3 @@
'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 fec5c7c..332d7f4 100644
--- a/erpnext/setup/doctype/authorization_control/authorization_control.py
+++ b/erpnext/setup/doctype/authorization_control/authorization_control.py
@@ -2,11 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cstr, flt, has_common, comma_or
-from frappe import session, _
+from frappe import _, session
+from frappe.utils import comma_or, cstr, flt, has_common
+
from erpnext.utilities.transaction_base import TransactionBase
+
class AuthorizationControl(TransactionBase):
def get_appr_user_role(self, det, doctype_name, total, based_on, condition, item, company):
amt_list, appr_users, appr_roles = [], [], []
diff --git a/erpnext/setup/doctype/authorization_rule/authorization_rule.py b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
index eb8e6eb..ab0f420 100644
--- a/erpnext/setup/doctype/authorization_rule/authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/authorization_rule.py
@@ -2,12 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-
-from frappe.utils import cstr, flt
-from frappe import _, msgprint
-
+from frappe import _
from frappe.model.document import Document
+from frappe.utils import cstr, flt
+
class AuthorizationRule(Document):
def check_duplicate_entry(self):
diff --git a/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py b/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
index 332f103..8a0f664 100644
--- a/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
+++ b/erpnext/setup/doctype/authorization_rule/test_authorization_rule.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Authorization Rule')
diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js
index 3878a79..3680906 100644
--- a/erpnext/setup/doctype/brand/brand.js
+++ b/erpnext/setup/doctype/brand/brand.js
@@ -1,13 +1,13 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-
+
//--------- ONLOAD -------------
cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
+
}
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
\ No newline at end of file
+
+}
diff --git a/erpnext/setup/doctype/brand/brand.py b/erpnext/setup/doctype/brand/brand.py
index 12839d1..4cfb018 100644
--- a/erpnext/setup/doctype/brand/brand.py
+++ b/erpnext/setup/doctype/brand/brand.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import copy
+import frappe
from frappe.model.document import Document
+
class Brand(Document):
pass
@@ -21,4 +23,4 @@
row.pop("name")
return row
- return frappe._dict()
\ No newline at end of file
+ return frappe._dict()
diff --git a/erpnext/setup/doctype/brand/test_brand.py b/erpnext/setup/doctype/brand/test_brand.py
index 265d2fe..16873c9 100644
--- a/erpnext/setup/doctype/brand/test_brand.py
+++ b/erpnext/setup/doctype/brand/test_brand.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Brand')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Brand')
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index d05541b..95ca386 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -204,43 +204,6 @@
}
}
-cur_frm.cscript.change_abbr = function() {
- var dialog = new frappe.ui.Dialog({
- title: "Replace Abbr",
- fields: [
- {"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr",
- "reqd": 1 },
- {"fieldtype": "Button", "label": "Update", "fieldname": "update"},
- ]
- });
-
- dialog.fields_dict.update.$input.click(function() {
- var args = dialog.get_values();
- if(!args) return;
- frappe.show_alert(__("Update in progress. It might take a while."));
- return frappe.call({
- method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr",
- args: {
- "company": cur_frm.doc.name,
- "old": cur_frm.doc.abbr,
- "new": args.new_abbr
- },
- callback: function(r) {
- if(r.exc) {
- frappe.msgprint(__("There were errors."));
- return;
- } else {
- cur_frm.set_value("abbr", args.new_abbr);
- }
- dialog.hide();
- cur_frm.refresh();
- },
- btn: this
- })
- });
- dialog.show();
-}
-
erpnext.company.setup_queries = function(frm) {
$.each([
["default_bank_account", {"account_type": "Bank"}],
@@ -313,4 +276,3 @@
frm.set_df_property("chart_of_accounts", "read_only", bool);
frm.set_df_property("existing_company", "read_only", bool);
}
-
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index e6ec496..63d96bf 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -12,33 +12,48 @@
"details",
"company_name",
"abbr",
- "change_abbr",
+ "default_currency",
+ "country",
"is_group",
"cb0",
- "domain",
- "parent_company",
- "charts_section",
- "default_currency",
"default_letter_head",
- "default_holiday_list",
- "default_finance_book",
- "default_selling_terms",
- "default_buying_terms",
- "default_warehouse_for_sales_return",
- "default_in_transit_warehouse",
- "column_break_10",
- "country",
- "create_chart_of_accounts_based_on",
- "chart_of_accounts",
- "existing_company",
"tax_id",
+ "domain",
"date_of_establishment",
+ "parent_company",
+ "company_info",
+ "company_logo",
+ "date_of_incorporation",
+ "phone_no",
+ "email",
+ "company_description",
+ "column_break1",
+ "date_of_commencement",
+ "fax",
+ "website",
+ "address_html",
+ "section_break_28",
+ "create_chart_of_accounts_based_on",
+ "existing_company",
+ "column_break_26",
+ "chart_of_accounts",
+ "charts_section",
"sales_settings",
- "monthly_sales_target",
+ "default_buying_terms",
"sales_monthly_history",
- "column_break_goals",
- "transactions_annual_history",
+ "monthly_sales_target",
"total_monthly_sales",
+ "column_break_goals",
+ "default_selling_terms",
+ "default_warehouse_for_sales_return",
+ "credit_limit",
+ "transactions_annual_history",
+ "hr_settings_section",
+ "default_holiday_list",
+ "default_expense_claim_payable_account",
+ "column_break_10",
+ "default_employee_advance_account",
+ "default_payroll_payable_account",
"default_settings",
"default_bank_account",
"default_cash_account",
@@ -52,24 +67,20 @@
"column_break0",
"allow_account_creation_against_child_company",
"default_payable_account",
- "default_employee_advance_account",
"default_expense_account",
"default_income_account",
"default_deferred_revenue_account",
"default_deferred_expense_account",
- "default_payroll_payable_account",
- "default_expense_claim_payable_account",
"default_discount_account",
- "section_break_22",
- "cost_center",
- "column_break_26",
- "credit_limit",
"payment_terms",
+ "cost_center",
+ "default_finance_book",
"auto_accounting_for_stock_settings",
"enable_perpetual_inventory",
"enable_perpetual_inventory_for_non_stock_items",
"default_inventory_account",
"stock_adjustment_account",
+ "default_in_transit_warehouse",
"column_break_32",
"stock_received_but_not_billed",
"service_received_but_not_billed",
@@ -79,25 +90,14 @@
"depreciation_expense_account",
"series_for_depreciation_entry",
"expenses_included_in_asset_valuation",
+ "repair_and_maintenance_account",
"column_break_40",
"disposal_account",
"depreciation_cost_center",
"capital_work_in_progress_account",
- "repair_and_maintenance_account",
"asset_received_but_not_billed",
"budget_detail",
"exception_budget_approver_role",
- "company_info",
- "company_logo",
- "date_of_incorporation",
- "address_html",
- "date_of_commencement",
- "phone_no",
- "fax",
- "email",
- "website",
- "column_break1",
- "company_description",
"registration_info",
"registration_details",
"lft",
@@ -125,13 +125,8 @@
"label": "Abbr",
"oldfieldname": "abbr",
"oldfieldtype": "Data",
- "reqd": 1
- },
- {
- "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")",
- "fieldname": "change_abbr",
- "fieldtype": "Button",
- "label": "Change Abbreviation"
+ "reqd": 1,
+ "set_only_once": 1
},
{
"bold": 1,
@@ -176,10 +171,9 @@
"label": "Company Description"
},
{
- "collapsible": 1,
"fieldname": "sales_settings",
"fieldtype": "Section Break",
- "label": "Sales Settings"
+ "label": "Buying & Selling Settings"
},
{
"fieldname": "sales_monthly_history",
@@ -443,10 +437,6 @@
"options": "Account"
},
{
- "fieldname": "section_break_22",
- "fieldtype": "Section Break"
- },
- {
"depends_on": "eval:!doc.__islocal",
"fieldname": "cost_center",
"fieldtype": "Link",
@@ -456,10 +446,6 @@
"options": "Cost Center"
},
{
- "fieldname": "column_break_26",
- "fieldtype": "Column Break"
- },
- {
"depends_on": "eval:!doc.__islocal",
"fieldname": "credit_limit",
"fieldtype": "Currency",
@@ -589,10 +575,10 @@
},
{
"collapsible": 1,
- "description": "For reference only.",
+ "depends_on": "eval: doc.docstatus == 0 && doc.__islocal != 1",
"fieldname": "company_info",
"fieldtype": "Section Break",
- "label": "Company Info"
+ "label": "Address & Contact"
},
{
"fieldname": "date_of_incorporation",
@@ -731,6 +717,15 @@
"options": "Account"
},
{
+ "fieldname": "hr_settings_section",
+ "fieldtype": "Section Break",
+ "label": "HR & Payroll Settings"
+ },
+ {
+ "fieldname": "column_break_26",
+ "fieldtype": "Column Break"
+ },
+ {
"collapsible": 1,
"fieldname": "fixed_asset_defaults",
"fieldtype": "Section Break",
@@ -741,6 +736,11 @@
"fieldtype": "Link",
"label": "Repair and Maintenance Account",
"options": "Account"
+ },
+ {
+ "fieldname": "section_break_28",
+ "fieldtype": "Section Break",
+ "label": "Chart of Accounts"
}
],
"icon": "fa fa-building",
@@ -748,10 +748,11 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
- "modified": "2021-05-12 16:51:08.187233",
+ "modified": "2021-10-04 12:09:25.833133",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
+ "naming_rule": "By fieldname",
"nsm_parent_field": "parent_company",
"owner": "Administrator",
"permissions": [
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 95cbf51..0b1b4a1 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -2,23 +2,24 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, os, json
-from frappe import _
-from frappe.utils import get_timestamp
-from frappe.utils import cint, today, formatdate
-import frappe.defaults
-from frappe.cache_manager import clear_defaults_cache
-
-from frappe.model.document import Document
-from frappe.contacts.address_and_contact import load_address_and_contact
-from frappe.utils.nestedset import NestedSet
-
-from past.builtins import cmp
import functools
+import json
+import os
+
+import frappe
+import frappe.defaults
+from frappe import _
+from frappe.cache_manager import clear_defaults_cache
+from frappe.contacts.address_and_contact import load_address_and_contact
+from frappe.utils import cint, formatdate, get_timestamp, today
+from frappe.utils.nestedset import NestedSet
+from past.builtins import cmp
+
from erpnext.accounts.doctype.account.account import get_account_currency
from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
+
class Company(NestedSet):
nsm_parent_field = 'parent_company'
@@ -108,7 +109,7 @@
frappe.flags.country_change = True
self.create_default_accounts()
self.create_default_warehouses()
-
+
if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
self.create_default_cost_center()
@@ -387,49 +388,16 @@
frappe.db.sql("delete from tabEmployee where company=%s", self.name)
frappe.db.sql("delete from tabDepartment where company=%s", self.name)
frappe.db.sql("delete from `tabTax Withholding Account` where company=%s", self.name)
+ frappe.db.sql("delete from `tabTransaction Deletion Record` where company=%s", self.name)
# delete tax templates
frappe.db.sql("delete from `tabSales Taxes and Charges Template` where company=%s", self.name)
frappe.db.sql("delete from `tabPurchase Taxes and Charges Template` where company=%s", self.name)
frappe.db.sql("delete from `tabItem Tax Template` where company=%s", self.name)
-@frappe.whitelist()
-def enqueue_replace_abbr(company, old, new):
- kwargs = dict(queue="long", company=company, old=old, new=new)
- frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs)
-
-
-@frappe.whitelist()
-def replace_abbr(company, old, new):
- new = new.strip()
- if not new:
- frappe.throw(_("Abbr can not be blank or space"))
-
- frappe.only_for("System Manager")
-
- def _rename_record(doc):
- parts = doc[0].rsplit(" - ", 1)
- if len(parts) == 1 or parts[1].lower() == old.lower():
- frappe.rename_doc(dt, doc[0], parts[0] + " - " + new, force=True)
-
- def _rename_records(dt):
- # rename is expensive so let's be economical with memory usage
- doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company))
- for d in doc:
- _rename_record(d)
- try:
- frappe.db.auto_commit_on_many_writes = 1
- frappe.db.set_value("Company", company, "abbr", new)
- for dt in ["Warehouse", "Account", "Cost Center", "Department",
- "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
- _rename_records(dt)
- frappe.db.commit()
-
- except Exception:
- frappe.log_error(title=_('Abbreviation Rename Error'))
- finally:
- frappe.db.auto_commit_on_many_writes = 0
-
+ # delete Process Deferred Accounts if no GL Entry found
+ 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 get_name_with_abbr(name, company):
company_abbr = frappe.get_cached_value('Company', company, "abbr")
@@ -475,8 +443,9 @@
def update_company_monthly_sales(company):
'''Cache past year monthly sales of every company based on sales invoices'''
- from frappe.utils.goal import get_monthly_results
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")
diff --git a/erpnext/setup/doctype/company/company_dashboard.py b/erpnext/setup/doctype/company/company_dashboard.py
index 9b483dd..3afea09 100644
--- a/erpnext/setup/doctype/company/company_dashboard.py
+++ b/erpnext/setup/doctype/company/company_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'graph': True,
@@ -37,4 +39,4 @@
'items': ['Project']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/setup/doctype/company/company_tree.js b/erpnext/setup/doctype/company/company_tree.js
index 19b276c..160481c 100644
--- a/erpnext/setup/doctype/company/company_tree.js
+++ b/erpnext/setup/doctype/company/company_tree.js
@@ -30,4 +30,4 @@
onload: function(treeview) {
treeview.make_tree();
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index e1c803a..abc4689 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -2,12 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
import json
+import unittest
+
+import frappe
from frappe import _
from frappe.utils import random_string
-from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import get_charts_for_country
+
+from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
+ get_charts_for_country,
+)
test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component", "Warehouse"]
test_dependencies = ["Fiscal Year"]
@@ -130,4 +134,3 @@
lead.company = company
lead.save()
return lead.name
-
diff --git a/erpnext/setup/doctype/company/tests/test_company.js b/erpnext/setup/doctype/company/tests/test_company.js
index 8c0b609..b568494 100644
--- a/erpnext/setup/doctype/company/tests/test_company.js
+++ b/erpnext/setup/doctype/company/tests/test_company.js
@@ -22,4 +22,4 @@
'chart of cost centers created'),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/setup/doctype/company/tests/test_company_production.js b/erpnext/setup/doctype/company/tests/test_company_production.js
index bf6e540..a4c1e2e 100644
--- a/erpnext/setup/doctype/company/tests/test_company_production.js
+++ b/erpnext/setup/doctype/company/tests/test_company_production.js
@@ -16,4 +16,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
index 6480f60..0b86e29 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -4,10 +4,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _, throw
from frappe.model.document import Document
-from frappe.utils import get_datetime_str, formatdate, nowdate, cint
+from frappe.utils import cint, formatdate, get_datetime_str, nowdate
+
class CurrencyExchange(Document):
def autoname(self):
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js
deleted file mode 100644
index 19fde2e..0000000
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Currency Exchange", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Currency Exchange
- () => frappe.tests.make('Currency Exchange', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
index 4ff2dd7..2b007e9 100644
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -1,14 +1,16 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-import frappe, unittest
-from frappe.utils import flt
+
+import unittest
+from unittest import mock
+
+import frappe
+from frappe.utils import cint, flt
+
from erpnext.setup.utils import get_exchange_rate
-from frappe.utils import cint
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
@@ -37,18 +39,45 @@
curr_exchange.for_selling = record["for_selling"]
curr_exchange.insert()
+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):
+ class PatchResponse:
+ def __init__(self, json_data, status_code):
+ self.json_data = json_data
+ self.status_code = status_code
+
+ def raise_for_status(self):
+ if self.status_code != 200:
+ raise frappe.DoesNotExistError
+
+ 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)
+
+ return PatchResponse({'result': None}, 404)
+
+@mock.patch('requests.get', side_effect=patched_requests_get)
class TestCurrencyExchange(unittest.TestCase):
def clear_cache(self):
cache = frappe.cache()
- key = "currency_exchange_rate:{0}:{1}".format("USD", "INR")
- cache.delete(key)
+ for date in test_exchange_values.keys():
+ key = "currency_exchange_rate_{0}:{1}:{2}".format(date, "USD", "INR")
+ cache.delete(key)
def tearDown(self):
frappe.db.set_value("Accounts Settings", None, "allow_stale", 1)
self.clear_cache()
- def test_exchange_rate(self):
+ def test_exchange_rate(self, mock_get):
save_new_records(test_records)
frappe.db.set_value("Accounts Settings", None, "allow_stale", 1)
@@ -69,7 +98,11 @@
self.assertFalse(exchange_rate == 60)
self.assertEqual(flt(exchange_rate, 3), 66.999)
- def test_exchange_rate_strict(self):
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-20", "for_buying")
+ self.assertFalse(exchange_rate == 60)
+ self.assertEqual(flt(exchange_rate, 3), 65.1)
+
+ def test_exchange_rate_strict(self, mock_get):
# strict currency settings
frappe.db.set_value("Accounts Settings", None, "allow_stale", 0)
frappe.db.set_value("Accounts Settings", None, "stale_days", 1)
@@ -79,7 +112,7 @@
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
- self.assertEqual(flt(exchange_rate, 3), 67.235)
+ self.assertEqual(flt(exchange_rate, 3), 65.100)
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
self.assertEqual(exchange_rate, 62.9)
@@ -89,7 +122,7 @@
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 66.999)
- def test_exchange_rate_strict_switched(self):
+ def test_exchange_rate_strict_switched(self, mock_get):
# Start with allow_stale is True
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
self.assertEqual(exchange_rate, 65.1)
@@ -97,7 +130,7 @@
frappe.db.set_value("Accounts Settings", None, "allow_stale", 0)
frappe.db.set_value("Accounts Settings", None, "stale_days", 1)
- # Will fetch from fixer.io
self.clear_cache()
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
- self.assertEqual(flt(exchange_rate, 3), 67.235)
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_buying")
+ self.assertFalse(exchange_rate == 65)
+ self.assertEqual(flt(exchange_rate, 3), 62.9)
diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py
index 68e1ccb..6e72810 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.py
+++ b/erpnext/setup/doctype/customer_group/customer_group.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-
-
from frappe.utils.nestedset import NestedSet, get_root_of
+
+
class CustomerGroup(NestedSet):
nsm_parent_field = 'parent_customer_group'
def validate(self):
@@ -30,4 +31,4 @@
order by lft asc""", (lft, rgt), as_dict=True)
def on_doctype_update():
- frappe.db.add_index("Customer Group", ["lft", "rgt"])
\ No newline at end of file
+ frappe.db.add_index("Customer Group", ["lft", "rgt"])
diff --git a/erpnext/setup/doctype/customer_group/customer_group_tree.js b/erpnext/setup/doctype/customer_group/customer_group_tree.js
index b52c79c..d50e9c8 100644
--- a/erpnext/setup/doctype/customer_group/customer_group_tree.js
+++ b/erpnext/setup/doctype/customer_group/customer_group_tree.js
@@ -1,3 +1,3 @@
frappe.treeview_settings["Customer Group"] = {
ignore_fields:["parent_customer_group"]
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/customer_group/test_customer_group.py b/erpnext/setup/doctype/customer_group/test_customer_group.py
index ec1af7a..e04b79b 100644
--- a/erpnext/setup/doctype/customer_group/test_customer_group.py
+++ b/erpnext/setup/doctype/customer_group/test_customer_group.py
@@ -7,4 +7,5 @@
import frappe
-test_records = frappe.get_test_records('Customer Group')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Customer Group')
diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js
index 1071ea2..c2c2710 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.js
+++ b/erpnext/setup/doctype/email_digest/email_digest.js
@@ -1,78 +1,31 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-cur_frm.cscript.refresh = function(doc, dt, dn) {
- doc = locals[dt][dn];
- cur_frm.add_custom_button(__('View Now'), function() {
- frappe.call({
- method: 'erpnext.setup.doctype.email_digest.email_digest.get_digest_msg',
- args: {
- name: doc.name
- },
- callback: function(r) {
- var d = new frappe.ui.Dialog({
- title: __('Email Digest: ') + dn,
- width: 800
+frappe.ui.form.on("Email Digest", {
+ refresh: function(frm) {
+ if (!frm.is_new()) {
+ frm.add_custom_button(__('View Now'), function() {
+ frappe.call({
+ method: 'erpnext.setup.doctype.email_digest.email_digest.get_digest_msg',
+ args: {
+ name: frm.doc.name
+ },
+ callback: function(r) {
+ let d = new frappe.ui.Dialog({
+ title: __('Email Digest: {0}', [frm.doc.name]),
+ width: 800
+ });
+ $(d.body).html(r.message);
+ d.show();
+ }
});
- $(d.body).html(r.message);
- d.show();
- }
- });
- }, "fa fa-eye-open", "btn-default");
-
- if (!cur_frm.is_new()) {
- cur_frm.add_custom_button(__('Send Now'), function() {
- return cur_frm.call('send', null, (r) => {
- frappe.show_alert(__('Message Sent'));
});
- });
+
+ frm.add_custom_button(__('Send Now'), function() {
+ return frm.call('send', null, () => {
+ frappe.show_alert({ message: __("Message Sent"), indicator: 'green'});
+ });
+ });
+ }
}
-};
-
-cur_frm.cscript.addremove_recipients = function(doc, dt, dn) {
- // Get user list
-
- return cur_frm.call('get_users', null, function(r) {
- // Open a dialog and display checkboxes against email addresses
- doc = locals[dt][dn];
- var d = new frappe.ui.Dialog({
- title: __('Add/Remove Recipients'),
- width: 400
- });
-
- $.each(r.user_list, function(i, v) {
- var fullname = frappe.user.full_name(v.name);
- if(fullname !== v.name) fullname = fullname + " <" + v.name + ">";
-
- if(v.enabled==0) {
- fullname = repl("<span style='color: red'> %(name)s (" + __("disabled user") + ")</span>", {name: v.name});
- }
-
- $('<div class="checkbox"><label>\
- <input type="checkbox" data-id="' + v.name + '"'+
- (v.checked ? 'checked' : '') +
- '> '+ fullname +'</label></div>').appendTo(d.body);
- });
-
- // Display add recipients button
- d.set_primary_action("Update", function() {
- cur_frm.cscript.add_to_rec_list(doc, d.body, r.user_list.length);
- });
-
- cur_frm.rec_dialog = d;
- d.show();
- });
-}
-
-cur_frm.cscript.add_to_rec_list = function(doc, dialog, length) {
- // add checked users to list of recipients
- var rec_list = [];
- $(dialog).find('input:checked').each(function(i, input) {
- rec_list.push($(input).attr('data-id'));
- });
-
- doc.recipient_list = rec_list.join('\n');
- cur_frm.rec_dialog.hide();
- cur_frm.save();
- cur_frm.refresh_fields();
-}
+});
diff --git a/erpnext/setup/doctype/email_digest/email_digest.json b/erpnext/setup/doctype/email_digest/email_digest.json
index 125aca1..06c98e5 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.json
+++ b/erpnext/setup/doctype/email_digest/email_digest.json
@@ -1,1482 +1,338 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "Prompt",
- "beta": 0,
- "creation": "2018-09-16 22:00:00",
- "custom": 0,
- "description": "Send regular summary reports via Email.",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "System",
- "editable_grid": 0,
+ "actions": [],
+ "autoname": "Prompt",
+ "creation": "2018-09-16 22:00:00",
+ "description": "Send regular summary reports via Email.",
+ "doctype": "DocType",
+ "document_type": "System",
+ "engine": "InnoDB",
+ "field_order": [
+ "settings",
+ "column_break0",
+ "enabled",
+ "company",
+ "frequency",
+ "next_send",
+ "column_break1",
+ "recipients",
+ "accounts",
+ "accounts_module",
+ "income",
+ "expenses_booked",
+ "income_year_to_date",
+ "expense_year_to_date",
+ "column_break_16",
+ "bank_balance",
+ "credit_balance",
+ "invoiced_amount",
+ "payables",
+ "work_in_progress",
+ "sales_orders_to_bill",
+ "purchase_orders_to_bill",
+ "operation",
+ "column_break_21",
+ "sales_order",
+ "purchase_order",
+ "sales_orders_to_deliver",
+ "purchase_orders_to_receive",
+ "sales_invoice",
+ "purchase_invoice",
+ "column_break_operation",
+ "new_quotations",
+ "pending_quotations",
+ "issue",
+ "project",
+ "purchase_orders_items_overdue",
+ "other",
+ "tools",
+ "calendar_events",
+ "todo_list",
+ "notifications",
+ "column_break_32",
+ "add_quote"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "settings",
- "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": "Email Digest Settings",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "settings",
+ "fieldtype": "Section Break",
+ "label": "Email Digest Settings"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break0",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break0",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "enabled",
- "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": "Enabled",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Enabled"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "For Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 1,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "For Company",
+ "options": "Company",
+ "remember_last_selected_value": 1,
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "frequency",
- "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": "How frequently?",
- "length": 0,
- "no_copy": 0,
- "options": "Daily\nWeekly\nMonthly",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "frequency",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "How frequently?",
+ "options": "Daily\nWeekly\nMonthly",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.enabled",
- "fieldname": "next_send",
- "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": "Next email will be sent on:",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:doc.enabled",
+ "fieldname": "next_send",
+ "fieldtype": "Data",
+ "label": "Next email will be sent on:",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break1",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Note: Email will not be sent to disabled users",
- "fieldname": "recipient_list",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Recipients",
- "length": 0,
- "no_copy": 0,
- "options": "Email",
- "permlevel": 0,
- "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
- },
+ "fieldname": "accounts",
+ "fieldtype": "Section Break",
+ "label": "Accounts"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "addremove_recipients",
- "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": "Add/Remove Recipients",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "accounts_module",
+ "fieldtype": "Column Break",
+ "hidden": 1,
+ "label": "Profit & Loss"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "accounts",
- "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": "Accounts",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "income",
+ "fieldtype": "Check",
+ "label": "New Income"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "accounts_module",
- "fieldtype": "Column Break",
- "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": "Profit & Loss",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "expenses_booked",
+ "fieldtype": "Check",
+ "label": "New Expenses"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "income",
- "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": "New Income",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "income_year_to_date",
+ "fieldtype": "Check",
+ "label": "Annual Income"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "expenses_booked",
- "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": "New Expenses",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "expense_year_to_date",
+ "fieldtype": "Check",
+ "label": "Annual Expenses"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "income_year_to_date",
- "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": "Annual Income",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break",
+ "label": "Balance Sheet"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "expense_year_to_date",
- "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": "Annual Expenses",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "bank_balance",
+ "fieldtype": "Check",
+ "label": "Bank Balance"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "column_break_16",
- "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": "Balance Sheet",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "credit_balance",
+ "fieldtype": "Check",
+ "label": "Bank Credit Balance"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "bank_balance",
- "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": "Bank Balance",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "invoiced_amount",
+ "fieldtype": "Check",
+ "label": "Receivables"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "credit_balance",
- "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": "Bank Credit Balance",
- "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
- },
+ "default": "0",
+ "fieldname": "payables",
+ "fieldtype": "Check",
+ "label": "Payables"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "invoiced_amount",
- "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": "Receivables",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "work_in_progress",
+ "fieldtype": "Column Break",
+ "label": "Work in Progress"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "payables",
- "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": "Payables",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "sales_orders_to_bill",
+ "fieldtype": "Check",
+ "label": "Sales Orders to Bill"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "work_in_progress",
- "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": "Work in Progress",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "purchase_orders_to_bill",
+ "fieldtype": "Check",
+ "label": "Purchase Orders to Bill"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sales_orders_to_bill",
- "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": "Sales Orders to Bill",
- "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": "operation",
+ "fieldtype": "Section Break",
+ "label": "Operations"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_orders_to_bill",
- "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": "Purchase Orders to Bill",
- "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_21",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "operation",
- "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": "Operations",
- "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
- },
+ "default": "0",
+ "fieldname": "sales_order",
+ "fieldtype": "Check",
+ "label": "New Sales Orders"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_21",
- "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
- },
+ "default": "0",
+ "fieldname": "purchase_order",
+ "fieldtype": "Check",
+ "label": "New Purchase Orders"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sales_order",
- "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": "New Sales Orders",
- "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
- },
+ "default": "0",
+ "fieldname": "sales_orders_to_deliver",
+ "fieldtype": "Check",
+ "label": "Sales Orders to Deliver"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_order",
- "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": "New Purchase Orders",
- "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
- },
+ "default": "0",
+ "fieldname": "purchase_orders_to_receive",
+ "fieldtype": "Check",
+ "label": "Purchase Orders to Receive"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sales_orders_to_deliver",
- "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": "Sales Orders to Deliver",
- "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
- },
+ "default": "0",
+ "fieldname": "sales_invoice",
+ "fieldtype": "Check",
+ "label": "New Sales Invoice"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_orders_to_receive",
- "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": "Purchase Orders to Receive",
- "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
- },
+ "default": "0",
+ "fieldname": "purchase_invoice",
+ "fieldtype": "Check",
+ "label": "New Purchase Invoice"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sales_invoice",
- "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": "New Sales Invoice",
- "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_operation",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_invoice",
- "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": "New Purchase Invoice",
- "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
- },
+ "default": "0",
+ "fieldname": "new_quotations",
+ "fieldtype": "Check",
+ "label": "New Quotations"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_operation",
- "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
- },
+ "default": "0",
+ "fieldname": "pending_quotations",
+ "fieldtype": "Check",
+ "label": "Open Quotations"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "new_quotations",
- "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": "New Quotations",
- "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
- },
+ "default": "0",
+ "fieldname": "issue",
+ "fieldtype": "Check",
+ "label": "Open Issues"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "pending_quotations",
- "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": "Open Quotations",
- "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
- },
+ "default": "0",
+ "fieldname": "project",
+ "fieldtype": "Check",
+ "label": "Open Projects"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "issue",
- "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": "Open Issues",
- "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
- },
+ "default": "0",
+ "fieldname": "purchase_orders_items_overdue",
+ "fieldtype": "Check",
+ "label": "Purchase Orders Items Overdue"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "project",
- "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": "Open Projects",
- "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": "other",
+ "fieldtype": "Section Break",
+ "label": "Other"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_orders_items_overdue",
- "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": "Purchase Orders Items Overdue",
- "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": "tools",
+ "fieldtype": "Column Break",
+ "label": "Tools"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "other",
- "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": "Other",
- "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
- },
+ "default": "0",
+ "fieldname": "calendar_events",
+ "fieldtype": "Check",
+ "label": "Upcoming Calendar Events"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "tools",
- "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": "Tools",
- "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
- },
+ "default": "0",
+ "fieldname": "todo_list",
+ "fieldtype": "Check",
+ "label": "Open To Do"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "calendar_events",
- "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": "Upcoming Calendar Events",
- "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
- },
+ "default": "0",
+ "fieldname": "notifications",
+ "fieldtype": "Check",
+ "label": "Open Notifications"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "todo_list",
- "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": "Open To Do",
- "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_32",
+ "fieldtype": "Column Break",
+ "label": " "
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "notifications",
- "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": "Open Notifications",
- "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
- },
+ "default": "0",
+ "fieldname": "add_quote",
+ "fieldtype": "Check",
+ "label": "Add Quote"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_32",
- "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": "add_quote",
- "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 Quote",
- "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": "Note: Email will not be sent to disabled users",
+ "fieldname": "recipients",
+ "fieldtype": "Table MultiSelect",
+ "label": "Recipients",
+ "options": "Email Digest Recipient",
+ "reqd": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-envelope",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2019-01-16 09:52:15.149908",
- "modified_by": "Administrator",
- "module": "Setup",
- "name": "Email Digest",
- "owner": "Administrator",
+ ],
+ "icon": "fa fa-envelope",
+ "idx": 1,
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-08-24 23:49:00.081695",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Email Digest",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 1,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "permlevel": 1,
+ "read": 1,
+ "role": "System Manager"
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 340d89b..4e9a8cc 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -2,19 +2,34 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.utils import (fmt_money, formatdate, format_time, now_datetime,
- get_url_to_form, get_url_to_list, flt, get_link_to_report, add_to_date, today)
+
from datetime import timedelta
-from dateutil.relativedelta import relativedelta
-from frappe.core.doctype.user.user import STANDARD_USERS
+
+import frappe
import frappe.desk.notifications
+from dateutil.relativedelta import relativedelta
+from frappe import _
+from frappe.core.doctype.user.user import STANDARD_USERS
+from frappe.utils import (
+ add_to_date,
+ flt,
+ fmt_money,
+ format_time,
+ formatdate,
+ get_link_to_report,
+ get_url_to_form,
+ get_url_to_list,
+ now_datetime,
+ today,
+)
+
from erpnext.accounts.utils import get_balance_on, get_count_on, get_fiscal_year
user_specific_content = ["calendar_events", "todo_list"]
from frappe.model.document import Document
+
+
class EmailDigest(Document):
def __init__(self, *args, **kwargs):
super(EmailDigest, self).__init__(*args, **kwargs)
@@ -47,28 +62,19 @@
# send email only to enabled users
valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser`
where enabled=1""")]
- recipients = list(filter(lambda r: r in valid_users,
- self.recipient_list.split("\n")))
- original_user = frappe.session.user
-
- if recipients:
- for user_id in recipients:
- frappe.set_user(user_id)
- frappe.set_user_lang(user_id)
+ if self.recipients:
+ for row in self.recipients:
msg_for_this_recipient = self.get_msg_html()
- if msg_for_this_recipient:
+ if msg_for_this_recipient and row.recipient in valid_users:
frappe.sendmail(
- recipients=user_id,
+ 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"))
- frappe.set_user(original_user)
- frappe.set_user_lang(original_user)
-
def get_msg_html(self):
"""Build email digest content"""
frappe.flags.ignore_account_permission = True
diff --git a/erpnext/setup/doctype/email_digest/quotes.py b/erpnext/setup/doctype/email_digest/quotes.py
index 95afe97..c77fe82 100644
--- a/erpnext/setup/doctype/email_digest/quotes.py
+++ b/erpnext/setup/doctype/email_digest/quotes.py
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
+
import random
+
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"),
@@ -32,4 +34,3 @@
]
return random.choice(quotes)
-
diff --git a/erpnext/setup/doctype/email_digest/templates/default.html b/erpnext/setup/doctype/email_digest/templates/default.html
index 4ee4b0f..666301a 100644
--- a/erpnext/setup/doctype/email_digest/templates/default.html
+++ b/erpnext/setup/doctype/email_digest/templates/default.html
@@ -180,8 +180,8 @@
<br>
</div>
{% endif %}
-
-<!-- Purchase Order Items Overdue -->
+
+<!-- Purchase Order Items Overdue -->
{% if purchase_orders_items_overdue_list %}
<h4 style="{{ section_head }}" class="text-center">{{ _("Purchase Order Items not received on time") }}</h4>
<div>
@@ -254,6 +254,6 @@
<div class="text-center">
<br><br><span class="text-danger">Please take necessary action</span>
</div>
-{% endif %}
-
+{% endif %}
+
</div>
diff --git a/erpnext/setup/doctype/email_digest/test_email_digest.py b/erpnext/setup/doctype/email_digest/test_email_digest.py
index afe693a..b3d0ce3 100644
--- a/erpnext/setup/doctype/email_digest/test_email_digest.py
+++ b/erpnext/setup/doctype/email_digest/test_email_digest.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Email Digest')
diff --git a/erpnext/healthcare/doctype/patient/__init__.py b/erpnext/setup/doctype/email_digest_recipient/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/patient/__init__.py
rename to erpnext/setup/doctype/email_digest_recipient/__init__.py
diff --git a/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json
new file mode 100644
index 0000000..8b2a6dc
--- /dev/null
+++ b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json
@@ -0,0 +1,33 @@
+{
+ "actions": [],
+ "creation": "2020-06-08 12:19:40.428949",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "recipient"
+ ],
+ "fields": [
+ {
+ "fieldname": "recipient",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Recipient",
+ "options": "User",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-08-24 23:10:23.217572",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Email Digest Recipient",
+ "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/setup/doctype/email_digest_recipient/email_digest_recipient.py b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py
new file mode 100644
index 0000000..5c8d695
--- /dev/null
+++ b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class EmailDigestRecipient(Document):
+ pass
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index a0ba1ef..66d3b9e 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
"""Global Defaults"""
import frappe
import frappe.defaults
-from frappe.utils import cint
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+from frappe.utils import cint
keydict = {
# "key in defaults": "key in Global Defaults"
@@ -22,6 +23,7 @@
from frappe.model.document import Document
+
class GlobalDefaults(Document):
def on_update(self):
diff --git a/erpnext/setup/doctype/global_defaults/test_global_defaults.js b/erpnext/setup/doctype/global_defaults/test_global_defaults.js
deleted file mode 100644
index 33634eb..0000000
--- a/erpnext/setup/doctype/global_defaults/test_global_defaults.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Global Defaults", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Global Defaults
- () => frappe.tests.make('Global Defaults', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/global_defaults/test_global_defaults.py b/erpnext/setup/doctype/global_defaults/test_global_defaults.py
index 0495af7..70a7c08 100644
--- a/erpnext/setup/doctype/global_defaults/test_global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/test_global_defaults.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestGlobalDefaults(unittest.TestCase):
pass
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index c46b6cc..ab50a58 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -2,19 +2,22 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import copy
+
+import frappe
from frappe import _
-from frappe.utils import nowdate, cint, cstr
+from frappe.utils import cint, cstr, nowdate
from frappe.utils.nestedset import NestedSet
-from frappe.website.website_generator import WebsiteGenerator
from frappe.website.utils import clear_cache
-from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
-from erpnext.shopping_cart.product_info import set_product_info_for_website
-from erpnext.utilities.product import get_qty_in_stock
+from frappe.website.website_generator import WebsiteGenerator
from six.moves.urllib.parse import quote
-from erpnext.shopping_cart.product_query import ProductQuery
+
from erpnext.shopping_cart.filters import ProductFiltersBuilder
+from erpnext.shopping_cart.product_info import set_product_info_for_website
+from erpnext.shopping_cart.product_query import ProductQuery
+from erpnext.utilities.product import get_qty_in_stock
+
class ItemGroup(NestedSet, WebsiteGenerator):
nsm_parent_field = 'parent_item_group'
@@ -36,6 +39,7 @@
self.parent_item_group = _('All Item Groups')
self.make_route()
+ self.validate_item_group_defaults()
def on_update(self):
NestedSet.on_update(self)
@@ -96,7 +100,7 @@
filter_engine = ProductFiltersBuilder(self.name)
context.field_filters = filter_engine.get_field_filters()
- context.attribute_filters = filter_engine.get_attribute_fitlers()
+ context.attribute_filters = filter_engine.get_attribute_filters()
context.update({
"parents": get_parent_item_groups(self.parent_item_group),
@@ -131,6 +135,10 @@
def delete_child_item_groups_key(self):
frappe.cache().hdel("child_item_groups", self.name)
+ 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)
+
@frappe.whitelist(allow_guest=True)
def get_product_list_for_group(product_group=None, start=0, limit=10, search=None):
if product_group:
diff --git a/erpnext/setup/doctype/item_group/item_group_tree.js b/erpnext/setup/doctype/item_group/item_group_tree.js
index 57afe02..b2628f4 100644
--- a/erpnext/setup/doctype/item_group/item_group_tree.js
+++ b/erpnext/setup/doctype/item_group/item_group_tree.js
@@ -1,3 +1,3 @@
frappe.treeview_settings["Item Group"] = {
ignore_fields:["parent_item_group"]
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/item_group/test_item_group.js b/erpnext/setup/doctype/item_group/test_item_group.js
deleted file mode 100644
index ea322e2..0000000
--- a/erpnext/setup/doctype/item_group/test_item_group.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Group", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Item Group
- () => frappe.tests.make('Item Group', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/item_group/test_item_group.py b/erpnext/setup/doctype/item_group/test_item_group.py
index 745b13a..a816f39 100644
--- a/erpnext/setup/doctype/item_group/test_item_group.py
+++ b/erpnext/setup/doctype/item_group/test_item_group.py
@@ -2,10 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import print_function, unicode_literals
+
import unittest
+
import frappe
-from frappe.utils.nestedset import NestedSetRecursionError, NestedSetMultipleRootsError, \
- NestedSetChildExistsError, NestedSetInvalidMergeError, rebuild_tree, get_ancestors_of
+from frappe.utils.nestedset import (
+ NestedSetChildExistsError,
+ NestedSetInvalidMergeError,
+ NestedSetMultipleRootsError,
+ NestedSetRecursionError,
+ get_ancestors_of,
+ rebuild_tree,
+)
test_records = frappe.get_test_records('Item Group')
diff --git a/erpnext/setup/doctype/item_group/test_records.json b/erpnext/setup/doctype/item_group/test_records.json
index 146da87..ce1d718 100644
--- a/erpnext/setup/doctype/item_group/test_records.json
+++ b/erpnext/setup/doctype/item_group/test_records.json
@@ -1,73 +1,74 @@
[
{
- "doctype": "Item Group",
- "is_group": 0,
- "item_group_name": "_Test Item Group",
+ "doctype": "Item Group",
+ "is_group": 0,
+ "item_group_name": "_Test Item Group",
"parent_item_group": "All Item Groups",
"item_group_defaults": [{
"company": "_Test Company",
"buying_cost_center": "_Test Cost Center 2 - _TC",
- "selling_cost_center": "_Test Cost Center 2 - _TC"
+ "selling_cost_center": "_Test Cost Center 2 - _TC",
+ "default_warehouse": "_Test Warehouse - _TC"
}]
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 0,
- "item_group_name": "_Test Item Group Desktops",
+ "doctype": "Item Group",
+ "is_group": 0,
+ "item_group_name": "_Test Item Group Desktops",
"parent_item_group": "All Item Groups"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group A",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group A",
"parent_item_group": "All Item Groups"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group B",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group B",
"parent_item_group": "All Item Groups"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group B - 1",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group B - 1",
"parent_item_group": "_Test Item Group B"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group B - 2",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group B - 2",
"parent_item_group": "_Test Item Group B"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 0,
- "item_group_name": "_Test Item Group B - 3",
+ "doctype": "Item Group",
+ "is_group": 0,
+ "item_group_name": "_Test Item Group B - 3",
"parent_item_group": "_Test Item Group B"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group C",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group C",
"parent_item_group": "All Item Groups"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group C - 1",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group C - 1",
"parent_item_group": "_Test Item Group C"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group C - 2",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group C - 2",
"parent_item_group": "_Test Item Group C"
- },
+ },
{
- "doctype": "Item Group",
- "is_group": 1,
- "item_group_name": "_Test Item Group D",
+ "doctype": "Item Group",
+ "is_group": 1,
+ "item_group_name": "_Test Item Group D",
"parent_item_group": "All Item Groups"
},
{
@@ -104,4 +105,4 @@
}
]
}
-]
\ 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
index c1f9433..005cfec 100644
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ b/erpnext/setup/doctype/naming_series/naming_series.py
@@ -2,15 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-
-from frappe.utils import cstr, cint
-from frappe import msgprint, throw, _
-
+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.core.doctype.doctype.doctype import validate_series
+from frappe.utils import cint, cstr
+
class NamingSeriesNotSetError(frappe.ValidationError): pass
@@ -79,7 +79,8 @@
options = self.scrub_options_list(ol)
# validate names
- for i in options: self.validate_series_name(i)
+ for i in options:
+ self.validate_series_name(i)
if options and self.user_must_always_select:
options = [''] + options
@@ -138,7 +139,7 @@
def validate_series_name(self, n):
import re
- if not re.match("^[\w\- /.#{}]*$", n, re.UNICODE):
+ if not re.match(r"^[\w\- \/.#{}]+$", n, re.UNICODE):
throw(_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series'))
@frappe.whitelist()
diff --git a/erpnext/setup/doctype/naming_series/test_naming_series.js b/erpnext/setup/doctype/naming_series/test_naming_series.js
deleted file mode 100644
index 22b664b..0000000
--- a/erpnext/setup/doctype/naming_series/test_naming_series.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Naming Series", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Naming Series
- () => frappe.tests.make('Naming Series', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/party_type/party_type.py b/erpnext/setup/doctype/party_type/party_type.py
index 96e6093..8424c7f 100644
--- a/erpnext/setup/doctype/party_type/party_type.py
+++ b/erpnext/setup/doctype/party_type/party_type.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class PartyType(Document):
pass
diff --git a/erpnext/setup/doctype/party_type/test_party_type.js b/erpnext/setup/doctype/party_type/test_party_type.js
deleted file mode 100644
index c97dbc5..0000000
--- a/erpnext/setup/doctype/party_type/test_party_type.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Party Type", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Party Type
- () => frappe.tests.make('Party Type', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/party_type/test_party_type.py b/erpnext/setup/doctype/party_type/test_party_type.py
index 079fe2f..e5f2908 100644
--- a/erpnext/setup/doctype/party_type/test_party_type.py
+++ b/erpnext/setup/doctype/party_type/test_party_type.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Party Type')
diff --git a/erpnext/setup/doctype/print_heading/print_heading.js b/erpnext/setup/doctype/print_heading/print_heading.js
index 3878a79..3680906 100644
--- a/erpnext/setup/doctype/print_heading/print_heading.js
+++ b/erpnext/setup/doctype/print_heading/print_heading.js
@@ -1,13 +1,13 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-
+
//--------- ONLOAD -------------
cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
+
}
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
\ No newline at end of file
+
+}
diff --git a/erpnext/setup/doctype/print_heading/print_heading.py b/erpnext/setup/doctype/print_heading/print_heading.py
index 00dc0f3..cf25638 100644
--- a/erpnext/setup/doctype/print_heading/print_heading.py
+++ b/erpnext/setup/doctype/print_heading/print_heading.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PrintHeading(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/setup/doctype/print_heading/test_print_heading.py b/erpnext/setup/doctype/print_heading/test_print_heading.py
index 59455d2..06f801a 100644
--- a/erpnext/setup/doctype/print_heading/test_print_heading.py
+++ b/erpnext/setup/doctype/print_heading/test_print_heading.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Print Heading')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Print Heading')
diff --git a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js
index 3878a79..3680906 100644
--- a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js
+++ b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js
@@ -1,13 +1,13 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-
+
//--------- ONLOAD -------------
cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
+
}
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
\ No newline at end of file
+
+}
diff --git a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py
index 2cc6235..9131cc3 100644
--- a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py
+++ b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class QuotationLostReason(Document):
- pass
\ No newline at end of file
+ pass
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 ff4c788..ab8d61f 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
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
-test_records = frappe.get_test_records('Quotation Lost Reason')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Quotation Lost Reason')
diff --git a/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py b/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py
index 7bb8d02..434f24e 100644
--- a/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py
+++ b/erpnext/setup/doctype/quotation_lost_reason_detail/quotation_lost_reason_detail.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QuotationLostReasonDetail(Document):
pass
diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py
index 675f9ca..6c741a8 100644
--- a/erpnext/setup/doctype/sales_partner/sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/sales_partner.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.utils import cstr, filter_strip_join
from frappe.website.website_generator import WebsiteGenerator
-from frappe.contacts.address_and_contact import load_address_and_contact
+
class SalesPartner(WebsiteGenerator):
website = frappe._dict(
diff --git a/erpnext/setup/doctype/sales_partner/test_sales_partner.py b/erpnext/setup/doctype/sales_partner/test_sales_partner.py
index 4548a4e..6ece239 100644
--- a/erpnext/setup/doctype/sales_partner/test_sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/test_sales_partner.py
@@ -2,8 +2,8 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
+
test_records = frappe.get_test_records('Sales Partner')
test_ignore = ["Item Group"]
diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py
index 19c2e5b..c7cad6b 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.py
+++ b/erpnext/setup/doctype/sales_person/sales_person.py
@@ -2,12 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils.nestedset import NestedSet, get_root_of
+
from erpnext import get_default_currency
+
class SalesPerson(NestedSet):
nsm_parent_field = 'parent_sales_person'
diff --git a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
index 3d0b2ff..61c1ba4 100644
--- a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
+++ b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
@@ -12,4 +14,4 @@
'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/setup/doctype/sales_person/sales_person_tree.js b/erpnext/setup/doctype/sales_person/sales_person_tree.js
index bcdfac9..00056fd 100644
--- a/erpnext/setup/doctype/sales_person/sales_person_tree.js
+++ b/erpnext/setup/doctype/sales_person/sales_person_tree.js
@@ -9,4 +9,4 @@
{fieldtype:'Check', fieldname:'is_group', label:__('Group Node'),
description: __("Further nodes can be only created under 'Group' type nodes")}
],
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/sales_person/test_sales_person.py b/erpnext/setup/doctype/sales_person/test_sales_person.py
index 8313cb4..497aaad 100644
--- a/erpnext/setup/doctype/sales_person/test_sales_person.py
+++ b/erpnext/setup/doctype/sales_person/test_sales_person.py
@@ -5,6 +5,7 @@
test_dependencies = ["Employee"]
import frappe
+
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 9d84f90..0ca3525 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.utils.nestedset import NestedSet, get_root_of
+
class SupplierGroup(NestedSet):
nsm_parent_field = 'parent_supplier_group'
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group_tree.js b/erpnext/setup/doctype/supplier_group/supplier_group_tree.js
index 0788e2e..728793e 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group_tree.js
+++ b/erpnext/setup/doctype/supplier_group/supplier_group_tree.js
@@ -1,4 +1,4 @@
frappe.treeview_settings["Supplier Group"] = {
breadcrumbs: "Buying",
ignore_fields:["parent_supplier_group"]
-};
\ No newline at end of file
+};
diff --git a/erpnext/setup/doctype/supplier_group/test_supplier_group.js b/erpnext/setup/doctype/supplier_group/test_supplier_group.js
deleted file mode 100644
index 976dd2c..0000000
--- a/erpnext/setup/doctype/supplier_group/test_supplier_group.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Supplier Group", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Supplier Group
- () => frappe.tests.make('Supplier Group', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/supplier_group/test_supplier_group.py b/erpnext/setup/doctype/supplier_group/test_supplier_group.py
index 0e3d23d..b3a6366 100644
--- a/erpnext/setup/doctype/supplier_group/test_supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/test_supplier_group.py
@@ -4,4 +4,5 @@
from __future__ import unicode_literals
import frappe
+
test_records = frappe.get_test_records('Supplier Group')
diff --git a/erpnext/setup/doctype/target_detail/target_detail.py b/erpnext/setup/doctype/target_detail/target_detail.py
index d2e2597..89cd814 100644
--- a/erpnext/setup/doctype/target_detail/target_detail.py
+++ b/erpnext/setup/doctype/target_detail/target_detail.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class TargetDetail(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js
index 3878a79..3680906 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js
@@ -1,13 +1,13 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-
+
//--------- ONLOAD -------------
cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
+
}
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
\ No newline at end of file
+
+}
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 372cc6d..8c9059f 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py
@@ -2,15 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
+import json
+
import frappe
from frappe import _, throw
-import json
from frappe.model.document import Document
-from frappe.utils.jinja import validate_template
from frappe.utils import cint
-
+from frappe.utils.jinja import validate_template
from six import string_types
+
class TermsandConditions(Document):
def validate(self):
if self.terms:
@@ -24,6 +26,6 @@
doc = json.loads(doc)
terms_and_conditions = frappe.get_doc("Terms and Conditions", template_name)
-
+
if terms_and_conditions.terms:
- return frappe.render_template(terms_and_conditions.terms, doc)
\ No newline at end of file
+ return frappe.render_template(terms_and_conditions.terms, doc)
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 6fea78f..abfa921 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
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
+
test_records = frappe.get_test_records('Terms and Conditions')
diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js
index ceec47ae..3caf814 100644
--- a/erpnext/setup/doctype/territory/territory.js
+++ b/erpnext/setup/doctype/territory/territory.js
@@ -36,4 +36,4 @@
['Territory', 'name', '!=', doc.territory_name]
]
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py
index 05e8f66..f61796b 100644
--- a/erpnext/setup/doctype/territory/territory.py
+++ b/erpnext/setup/doctype/territory/territory.py
@@ -2,12 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt
-from frappe import _
+import frappe
+from frappe import _
+from frappe.utils import flt
from frappe.utils.nestedset import NestedSet, get_root_of
+
class Territory(NestedSet):
nsm_parent_field = 'parent_territory'
@@ -24,4 +25,4 @@
self.validate_one_root()
def on_doctype_update():
- frappe.db.add_index("Territory", ["lft", "rgt"])
\ No newline at end of file
+ frappe.db.add_index("Territory", ["lft", "rgt"])
diff --git a/erpnext/setup/doctype/territory/territory_tree.js b/erpnext/setup/doctype/territory/territory_tree.js
index edd11df..dadeeef 100644
--- a/erpnext/setup/doctype/territory/territory_tree.js
+++ b/erpnext/setup/doctype/territory/territory_tree.js
@@ -1,3 +1,3 @@
frappe.treeview_settings["Territory"] = {
ignore_fields:["parent_territory"]
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/territory/test_territory.py b/erpnext/setup/doctype/territory/test_territory.py
index efe00c5..a3aa866 100644
--- a/erpnext/setup/doctype/territory/test_territory.py
+++ b/erpnext/setup/doctype/territory/test_territory.py
@@ -2,8 +2,8 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
+
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 bbe6836..aa0f79b 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
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestTransactionDeletionRecord(unittest.TestCase):
def setUp(self):
create_company('Dunder Mifflin Paper Co')
@@ -23,7 +25,7 @@
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')
@@ -40,13 +42,13 @@
'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)
def create_transaction_deletion_request(company):
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
index 20caa15..6a50ef8 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
@@ -4,7 +4,7 @@
frappe.ui.form.on('Transaction Deletion Record', {
onload: function(frm) {
if (frm.doc.docstatus == 0) {
- let doctypes_to_be_ignored_array;
+ let doctypes_to_be_ignored_array;
frappe.call({
method: 'erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.get_doctypes_to_be_ignored',
callback: function(r) {
@@ -25,15 +25,15 @@
frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
frm.refresh_field('doctypes_to_be_ignored');
}
-
+
});
function populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm) {
if (!(frm.doc.doctypes_to_be_ignored)) {
var i;
- for (i = 0; i < doctypes_to_be_ignored_array.length; i++) {
+ for (i = 0; i < doctypes_to_be_ignored_array.length; i++) {
frm.add_child('doctypes_to_be_ignored', {
- doctype_name: doctypes_to_be_ignored_array[i]
+ doctype_name: doctypes_to_be_ignored_array[i]
});
}
}
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 8a49155..efb038f 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -3,11 +3,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from frappe.utils import cint
+
import frappe
-from frappe.model.document import Document
from frappe import _
from frappe.desk.notifications import clear_notifications
+from frappe.model.document import Document
+from frappe.utils import cint
+
class TransactionDeletionRecord(Document):
def validate(self):
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
index d7175dd..c238f18 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
@@ -9,4 +9,4 @@
return [__("Completed"), "green"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
index 2176cb1..a113d50 100644
--- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class TransactionDeletionRecordItem(Document):
pass
diff --git a/erpnext/setup/doctype/uom/test_uom.py b/erpnext/setup/doctype/uom/test_uom.py
index 330d303..e222c13 100644
--- a/erpnext/setup/doctype/uom/test_uom.py
+++ b/erpnext/setup/doctype/uom/test_uom.py
@@ -2,6 +2,6 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-
import frappe
+
test_records = frappe.get_test_records('UOM')
diff --git a/erpnext/setup/doctype/uom/uom.js b/erpnext/setup/doctype/uom/uom.js
index 3878a79..3680906 100644
--- a/erpnext/setup/doctype/uom/uom.js
+++ b/erpnext/setup/doctype/uom/uom.js
@@ -1,13 +1,13 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-
+
//--------- ONLOAD -------------
cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
+
}
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
\ No newline at end of file
+
+}
diff --git a/erpnext/setup/doctype/uom/uom.py b/erpnext/setup/doctype/uom/uom.py
index f7f86d6..f0e97b3 100644
--- a/erpnext/setup/doctype/uom/uom.py
+++ b/erpnext/setup/doctype/uom/uom.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class UOM(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.js b/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.js
deleted file mode 100644
index afcf74c..0000000
--- a/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: UOM Conversion Factor", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new UOM Conversion Factor
- () => frappe.tests.make('UOM Conversion Factor', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.py b/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.py
index 04596ef..33795d6 100644
--- a/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.py
+++ b/erpnext/setup/doctype/uom_conversion_factor/test_uom_conversion_factor.py
@@ -5,5 +5,6 @@
import unittest
+
class TestUOMConversionFactor(unittest.TestCase):
pass
diff --git a/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.py b/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.py
index 3566c53..45342e9 100644
--- a/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.py
+++ b/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class UOMConversionFactor(Document):
pass
diff --git a/erpnext/setup/doctype/website_item_group/website_item_group.py b/erpnext/setup/doctype/website_item_group/website_item_group.py
index 9ac7df2..2f72013 100644
--- a/erpnext/setup/doctype/website_item_group/website_item_group.py
+++ b/erpnext/setup/doctype/website_item_group/website_item_group.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class WebsiteItemGroup(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index bbee74c..cdc83c1 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -4,16 +4,18 @@
from __future__ import print_function, unicode_literals
import frappe
-from erpnext.accounts.doctype.cash_flow_mapper.default_cash_flow_mapper import DEFAULT_MAPPERS
-from .default_success_action import get_default_success_action
from frappe import _
-from frappe.utils import cint
-from frappe.installer import update_site_config
-from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-from erpnext.setup.default_energy_point_rules import get_default_energy_point_rules
+from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
+from frappe.installer import update_site_config
+from frappe.utils import cint
from six import iteritems
+from erpnext.accounts.doctype.cash_flow_mapper.default_cash_flow_mapper import DEFAULT_MAPPERS
+from erpnext.setup.default_energy_point_rules import get_default_energy_point_rules
+
+from .default_success_action import get_default_success_action
+
default_mail_footer = """<div style="padding: 7px; text-align: right; color: #888"><small>Sent via
<a style="color: #888" href="http://erpnext.org">ERPNext</a></div>"""
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 34af093..b7e895d 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -2116,9 +2116,9 @@
},
"Saudi Arabia": {
- "KSA VAT 5%": {
- "account_name": "VAT 5%",
- "tax_rate": 5.00
+ "KSA VAT 15%": {
+ "account_name": "VAT 15%",
+ "tax_rate": 15.00
},
"KSA VAT Zero": {
"account_name": "VAT Zero",
diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py
index 9ce64eb..5369bba 100644
--- a/erpnext/setup/setup_wizard/data/dashboard_charts.py
+++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
-from frappe import _
-import frappe
+
import json
+import frappe
+
+
def get_company_for_dashboards():
company = frappe.defaults.get_defaults().company
if company:
diff --git a/erpnext/setup/setup_wizard/data/industry_type.py b/erpnext/setup/setup_wizard/data/industry_type.py
index 4fa9f8a..2c83a5c 100644
--- a/erpnext/setup/setup_wizard/data/industry_type.py
+++ b/erpnext/setup/setup_wizard/data/industry_type.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_industry_types():
return [
_('Accounting'),
diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py
index 4edf948..bea3906 100644
--- a/erpnext/setup/setup_wizard/operations/company_setup.py
+++ b/erpnext/setup/setup_wizard/operations/company_setup.py
@@ -6,7 +6,6 @@
from frappe import _
from frappe.utils import cstr, getdate
from .default_website import website_maker
-from erpnext.accounts.doctype.account.account import RootNotEditable
def create_fiscal_year_and_company(args):
if (args.get('fy_start_date')):
@@ -45,9 +44,16 @@
def create_email_digest():
from frappe.utils.user import get_system_managers
system_managers = get_system_managers(only_name=True)
+
if not system_managers:
return
+ recipients = []
+ for d in system_managers:
+ 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):
@@ -56,7 +62,7 @@
"name": "Default Weekly Digest - " + company,
"company": company,
"frequency": "Weekly",
- "recipient_list": "\n".join(system_managers)
+ "recipients": recipients
})
for df in edigest.meta.get("fields", {"fieldtype": "Check"}):
@@ -72,7 +78,7 @@
"name": "Scheduler Errors",
"company": companies[0],
"frequency": "Daily",
- "recipient_list": "\n".join(system_managers),
+ "recipients": recipients,
"scheduler_errors": 1,
"enabled": 1
})
diff --git a/erpnext/setup/setup_wizard/operations/default_website.py b/erpnext/setup/setup_wizard/operations/default_website.py
index 38b5c14..2288ae0 100644
--- a/erpnext/setup/setup_wizard/operations/default_website.py
+++ b/erpnext/setup/setup_wizard/operations/default_website.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe import _
from frappe.utils import nowdate
+
class website_maker(object):
def __init__(self, args):
self.args = args
diff --git a/erpnext/setup/setup_wizard/operations/defaults_setup.py b/erpnext/setup/setup_wizard/operations/defaults_setup.py
index 6dd0fb1..55d5ec8 100644
--- a/erpnext/setup/setup_wizard/operations/defaults_setup.py
+++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py
@@ -62,6 +62,13 @@
hr_settings.emp_created_by = "Naming Series"
hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
hr_settings.leave_status_notification_template = _("Leave Status Notification")
+
+ hr_settings.send_interview_reminder = 1
+ hr_settings.interview_reminder_template = _("Interview Reminder")
+ hr_settings.remind_before = "00:15:00"
+
+ hr_settings.send_interview_feedback_reminder = 1
+ hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
hr_settings.save()
def set_no_copy_fields_in_variant_settings():
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index cd49a18..c473395 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -3,16 +3,20 @@
from __future__ import unicode_literals
-import frappe, os, json
+import json
+import os
+import frappe
from frappe import _
+from frappe.desk.doctype.global_search_settings.global_search_settings import (
+ update_global_search_doctypes,
+)
from frappe.desk.page.setup_wizard.setup_wizard import make_records
from frappe.utils import cstr, getdate
-from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes
+from frappe.utils.nestedset import rebuild_tree
from erpnext.accounts.doctype.account.account import RootNotEditable
from erpnext.regional.address_template.setup import set_up_address_templates
-from frappe.utils.nestedset import rebuild_tree
default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
"Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing",
@@ -260,16 +264,26 @@
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"))
- records += [{'doctype': 'Email Template', 'name': _("Leave Approval Notification"), 'response': response,\
+ 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,\
+ 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"))
+
+ 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"))
+
+ records += [{'doctype': 'Email Template', 'name': _('Interview Feedback Reminder'), 'response': response,
+ 'subject': _('Interview Feedback Reminder'), '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"))
- records += [{'doctype': 'Email Template', 'name': _("Dispatch Notification"), 'response': response,\
+ 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
@@ -313,6 +327,14 @@
hr_settings.emp_created_by = "Naming Series"
hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
hr_settings.leave_status_notification_template = _("Leave Status Notification")
+
+ hr_settings.send_interview_reminder = 1
+ hr_settings.interview_reminder_template = _("Interview Reminder")
+ hr_settings.remind_before = "00:15:00"
+
+ hr_settings.send_interview_feedback_reminder = 1
+ hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
+
hr_settings.save()
def update_item_variant_settings():
diff --git a/erpnext/setup/setup_wizard/operations/sample_data.py b/erpnext/setup/setup_wizard/operations/sample_data.py
index c11a388..3aef40d 100644
--- a/erpnext/setup/setup_wizard/operations/sample_data.py
+++ b/erpnext/setup/setup_wizard/operations/sample_data.py
@@ -3,11 +3,15 @@
from __future__ import unicode_literals
+import json
+import os
+import random
+
import frappe
-from frappe.utils.make_random import add_random_children
import frappe.utils
-import random, os, json
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
@@ -173,4 +177,4 @@
frappe.db.sql('delete from tabProject')
frappe.db.sql('delete from tabTask')
make_projects('Education')
- import_notification()
\ No newline at end of file
+ import_notification()
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index bacada9..58a14d2 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -145,7 +145,7 @@
doc = frappe.get_doc(template)
- # Data in country wise json is already pre validated, hence validations can be ignored
+ # Data in country wise json is already pre validated, hence validations can be ignored
# Ingone validations to make doctypes faster
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
@@ -177,7 +177,7 @@
doc = frappe.get_doc(template)
- # Data in country wise json is already pre validated, hence validations can be ignored
+ # Data in country wise json is already pre validated, hence validations can be ignored
# Ingone validations to make doctypes faster
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
@@ -192,7 +192,7 @@
default_root_type = 'Liability'
root_type = account.get('root_type', default_root_type)
- existing_accounts = frappe.get_list('Account',
+ existing_accounts = frappe.get_all('Account',
filters={
'company': company_name,
'root_type': root_type
@@ -247,7 +247,7 @@
# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
# below the root account
- root_account = frappe.get_list('Account', {
+ root_account = frappe.get_all('Account', {
'is_group': 1,
'root_type': root_type,
'company': company_name,
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index f63d269..ecb07d5 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -6,7 +6,10 @@
import frappe
from frappe import _
-from .operations import install_fixtures as fixtures, company_setup, sample_data
+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"):
@@ -106,7 +109,7 @@
def make_sample_data(domains):
try:
sample_data.make_sample_data(domains)
- except:
+ except Exception:
# clear message
if frappe.message_log:
frappe.message_log.pop()
diff --git a/erpnext/setup/setup_wizard/utils.py b/erpnext/setup/setup_wizard/utils.py
index 4223f00..30b88ae 100644
--- a/erpnext/setup/setup_wizard/utils.py
+++ b/erpnext/setup/setup_wizard/utils.py
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
-import json, os
+
+import json
+import os
from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
-from erpnext.setup.setup_wizard import setup_wizard
+
def complete():
with open(os.path.join(os.path.dirname(__file__),
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index e49259e..2302a66 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, add_days
-from frappe.utils import get_datetime_str, nowdate
+from frappe.utils import add_days, flt, get_datetime_str, nowdate
+
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`
@@ -109,7 +111,7 @@
value = response.json()["result"]
cache.setex(name=key, time=21600, value=flt(value))
return flt(value)
- except:
+ 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))
return 0.0
diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json
index cc9569f..a4e7ad8 100644
--- a/erpnext/setup/workspace/home/home.json
+++ b/erpnext/setup/workspace/home/home.json
@@ -1,7 +1,7 @@
{
"category": "",
"charts": [],
- "content": "[{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Human Resources\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
+ "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Human Resources\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
"creation": "2020-01-23 13:46:38.833076",
"developer_mode_only": 0,
"disable_user_customization": 0,
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index 56afe95..3f1dfde 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -2,16 +2,20 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import throw, _
import frappe.defaults
-from frappe.utils import cint, flt, get_fullname, cstr
+from frappe import _, throw
from frappe.contacts.doctype.address.address import get_address_display
-from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import get_shopping_cart_settings
-from frappe.utils.nestedset import get_root_of
-from erpnext.accounts.utils import get_account_name
-from erpnext.utilities.product import get_qty_in_stock
from frappe.contacts.doctype.contact.contact import get_contact_name
+from frappe.utils import cint, cstr, flt, get_fullname
+from frappe.utils.nestedset import get_root_of
+
+from erpnext.accounts.utils import get_account_name
+from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ get_shopping_cart_settings,
+)
+from erpnext.utilities.product import get_qty_in_stock
class WebsitePriceListMissingError(frappe.ValidationError):
@@ -308,7 +312,7 @@
party = get_party()
party.customer_name = company_name or fullname
- party.customer_type == "Company" if company_name else "Individual"
+ party.customer_type = "Company" if company_name else "Individual"
contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user})
contact = frappe.get_doc("Contact", contact_name)
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py
index c069b90..8f4afda 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py
@@ -4,11 +4,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _, msgprint
-from frappe.utils import comma_and
+from frappe import _
from frappe.model.document import Document
-from frappe.utils import get_datetime, get_datetime_str, now_datetime
+from frappe.utils import flt
+
class ShoppingCartSetupError(frappe.ValidationError): pass
@@ -18,46 +19,35 @@
def validate(self):
if self.enabled:
- self.validate_exchange_rates_exist()
+ self.validate_price_list_exchange_rate()
- def validate_exchange_rates_exist(self):
- """check if exchange rates exist for all Price List currencies (to company's currency)"""
- company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
+ 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
+
+ company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
+ price_list_currency = frappe.db.get_value("Price List", self.price_list, "currency")
+
if not company_currency:
- msgprint(_("Please specify currency in Company") + ": " + self.company,
- raise_exception=ShoppingCartSetupError)
+ msg = f"Please specify currency in Company {self.company}"
+ frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError)
- price_list_currency_map = frappe.db.get_values("Price List",
- [self.price_list], "currency")
+ if not price_list_currency:
+ msg = f"Please specify currency in Price List {frappe.bold(self.price_list)}"
+ frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError)
- price_list_currency_map = dict(price_list_currency_map)
-
- # check if all price lists have a currency
- for price_list, currency in price_list_currency_map.items():
- if not currency:
- frappe.throw(_("Currency is required for Price List {0}").format(price_list))
+ if price_list_currency != company_currency:
+ from_currency, to_currency = price_list_currency, company_currency
- expected_to_exist = [currency + "-" + company_currency
- for currency in price_list_currency_map.values()
- if currency != company_currency]
+ # Get exchange rate checks Currency Exchange Records too
+ exchange_rate = get_exchange_rate(from_currency, to_currency, args="for_selling")
- # manqala 20/09/2016: set up selection parameters for query from tabCurrency Exchange
- from_currency = [currency for currency in price_list_currency_map.values() if currency != company_currency]
- to_currency = company_currency
- # manqala end
-
- if expected_to_exist:
- # manqala 20/09/2016: modify query so that it uses date in the selection from Currency Exchange.
- # exchange rates defined with date less than the date on which this document is being saved will be selected
- exists = frappe.db.sql_list("""select CONCAT(from_currency,'-',to_currency) from `tabCurrency Exchange`
- where from_currency in (%s) and to_currency = "%s" and date <= curdate()""" % (", ".join(["%s"]*len(from_currency)), to_currency), tuple(from_currency))
- # manqala end
-
- missing = list(set(expected_to_exist).difference(exists))
-
- if missing:
- msgprint(_("Missing Currency Exchange Rates for {0}").format(comma_and(missing)),
- raise_exception=ShoppingCartSetupError)
+ if not flt(exchange_rate):
+ msg = f"Missing Currency Exchange Rates for {from_currency}-{to_currency}"
+ 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"):
@@ -71,7 +61,7 @@
def get_shipping_rules(self, shipping_territory):
return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule")
-def validate_cart_settings(doc, method):
+def validate_cart_settings(doc=None, method=None):
frappe.get_doc("Shopping Cart Settings", "Shopping Cart Settings").run_method("validate")
def get_shopping_cart_settings():
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.js
deleted file mode 100644
index c8485e7..0000000
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Shopping Cart Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Shopping Cart Settings
- () => frappe.tests.make('Shopping Cart Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
index 75899e1..f8a22b0 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
@@ -4,9 +4,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
-from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import ShoppingCartSetupError
+
+import frappe
+
+from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ ShoppingCartSetupError,
+)
+
class TestShoppingCartSettings(unittest.TestCase):
def setUp(self):
@@ -16,17 +22,25 @@
return frappe.get_doc({"doctype": "Shopping Cart Settings",
"company": "_Test Company"})
- def test_exchange_rate_exists(self):
- frappe.db.sql("""delete from `tabCurrency Exchange`""")
+ # 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
- 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)
+ # def test_exchange_rate_exists(self):
+ # frappe.db.sql("""delete from `tabCurrency Exchange`""")
- 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()
+ # cart_settings = self.get_cart_settings()
+ # cart_settings.price_list = "_Test Price List Rest of the World"
+ # self.assertRaises(ShoppingCartSetupError, cart_settings.validate_price_list_exchange_rate)
+
+ # 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_price_list_exchange_rate()
def test_tax_rule_validation(self):
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
@@ -36,7 +50,7 @@
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")
-test_dependencies = ["Tax Rule"]
\ No newline at end of file
+test_dependencies = ["Tax Rule"]
diff --git a/erpnext/shopping_cart/filters.py b/erpnext/shopping_cart/filters.py
index 7dfa09e..4787ae5 100644
--- a/erpnext/shopping_cart/filters.py
+++ b/erpnext/shopping_cart/filters.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import _dict
+
class ProductFiltersBuilder:
def __init__(self, item_group=None):
@@ -55,37 +56,31 @@
return filter_data
- def get_attribute_fitlers(self):
+ def get_attribute_filters(self):
attributes = [row.attribute for row in self.doc.filter_attributes]
- attribute_docs = [
- frappe.get_doc('Item Attribute', attribute) for attribute in attributes
- ]
- valid_attributes = []
+ if not attributes:
+ return []
- for attr_doc in attribute_docs:
- selected_attributes = []
- for attr in attr_doc.item_attribute_values:
- or_filters = []
- filters= [
- ["Item Variant Attribute", "attribute", "=", attr.parent],
- ["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
- ]
- if self.item_group:
- or_filters.extend([
- ["item_group", "=", self.item_group],
- ["Website Item Group", "item_group", "=", self.item_group]
- ])
+ result = frappe.db.sql(
+ """
+ select
+ distinct attribute, attribute_value
+ from
+ `tabItem Variant Attribute`
+ where
+ attribute in %(attributes)s
+ and attribute_value is not null
+ """,
+ {"attributes": attributes},
+ as_dict=1,
+ )
- if frappe.db.get_all("Item", filters, or_filters=or_filters, limit=1):
- selected_attributes.append(attr)
+ attribute_value_map = {}
+ for d in result:
+ attribute_value_map.setdefault(d.attribute, []).append(d.attribute_value)
- if selected_attributes:
- valid_attributes.append(
- _dict(
- item_attribute_values=selected_attributes,
- name=attr_doc.name
- )
- )
-
- return valid_attributes
+ out = []
+ for name, values in attribute_value_map.items():
+ out.append(frappe._dict(name=name, item_attribute_values=values))
+ return out
diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py
index 29617a8..fa68636 100644
--- a/erpnext/shopping_cart/product_info.py
+++ b/erpnext/shopping_cart/product_info.py
@@ -4,10 +4,14 @@
from __future__ import unicode_literals
import frappe
+
from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list
-from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \
- import get_shopping_cart_settings, show_quantity_in_website
-from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status
+from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ get_shopping_cart_settings,
+ show_quantity_in_website,
+)
+from erpnext.utilities.product import get_non_stock_item_status, get_price, get_qty_in_stock
+
@frappe.whitelist(allow_guest=True)
def get_product_info_for_website(item_code, skip_quotation_creation=False):
@@ -66,4 +70,4 @@
item["price_sales_uom"] = product_info.get("price").get("formatted_price_sales_uom")
else:
item["price_stock_uom"] = ""
- item["price_sales_uom"] = ""
\ No newline at end of file
+ item["price_sales_uom"] = ""
diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py
index 6c92d96..5cc0505 100644
--- a/erpnext/shopping_cart/product_query.py
+++ b/erpnext/shopping_cart/product_query.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
import frappe
+
from erpnext.shopping_cart.product_info import get_product_info_for_website
+
class ProductQuery:
"""Query engine for product listing
diff --git a/erpnext/shopping_cart/search.py b/erpnext/shopping_cart/search.py
index 63e9fe1..5d2de78 100644
--- a/erpnext/shopping_cart/search.py
+++ b/erpnext/shopping_cart/search.py
@@ -1,9 +1,9 @@
import frappe
from frappe.search.full_text_search import FullTextSearch
-from whoosh.fields import TEXT, ID, KEYWORD, Schema
from frappe.utils import strip_html_tags
-from whoosh.qparser import MultifieldParser, FieldsPlugin, WildcardPlugin
from whoosh.analysis import StemmingAnalyzer
+from whoosh.fields import ID, KEYWORD, TEXT, Schema
+from whoosh.qparser import FieldsPlugin, MultifieldParser, WildcardPlugin
from whoosh.query import Prefix
INDEX_NAME = "products"
@@ -123,4 +123,4 @@
def build_index_for_all_routes():
search = ProductSearch(INDEX_NAME)
- return search.build()
\ No newline at end of file
+ return search.build()
diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py
index ac61aeb..d1284cd 100644
--- a/erpnext/shopping_cart/test_shopping_cart.py
+++ b/erpnext/shopping_cart/test_shopping_cart.py
@@ -2,12 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from frappe.utils import nowdate, add_months
-from erpnext.shopping_cart.cart import _get_cart_quotation, update_cart, get_party
+from frappe.utils import add_months, nowdate
+
+from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule
+from erpnext.shopping_cart.cart import _get_cart_quotation, get_party, update_cart
from erpnext.tests.utils import create_test_contact_and_address
-from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule
# test_dependencies = ['Payment Terms Template']
diff --git a/erpnext/shopping_cart/utils.py b/erpnext/shopping_cart/utils.py
index 3241234..f412e61 100644
--- a/erpnext/shopping_cart/utils.py
+++ b/erpnext/shopping_cart/utils.py
@@ -4,8 +4,11 @@
from __future__ import unicode_literals
import frappe
-import frappe.defaults
-from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import is_cart_enabled
+
+from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ is_cart_enabled,
+)
+
def show_cart_count():
if (is_cart_enabled() and
@@ -38,4 +41,4 @@
if link.link_doctype in ('Customer', 'Supplier'):
return link.link_doctype, link.link_name
- return 'Customer', None
\ No newline at end of file
+ return 'Customer', None
diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html
index 1b39534..e560f4a 100644
--- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html
+++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html
@@ -1,7 +1,7 @@
{%- macro slide(image, title, subtitle, action, label, index, align="Left", theme="Dark") -%}
{%- set align_class = resolve_class({
'text-right': align == 'Right',
- 'text-centre': align == 'Center',
+ 'text-centre': align == 'Centre',
'text-left': align == 'Left',
}) -%}
@@ -15,7 +15,7 @@
<div class="carousel-body container d-flex {{ align_class }}">
<div class="carousel-content align-self-center">
{%- if title -%}<h1 class="{{ heading_class }}">{{ title }}</h1>{%- endif -%}
- {%- if subtitle -%}<p class="text-muted mt-2">{{ subtitle }}</p>{%- endif -%}
+ {%- if subtitle -%}<p class="{{ heading_class }} mt-2">{{ subtitle }}</p>{%- endif -%}
{%- if action -%}
<a href="{{ action }}" class="btn btn-primary mt-3">
{{ label }}
@@ -27,12 +27,14 @@
</div>
{%- endmacro -%}
-<div id="{{ slider_name }}" class="section-carousel carousel slide" data-ride="carousel">
+{%- set hero_slider_id = 'id-' + frappe.utils.generate_hash('HeroSlider', 12) -%}
+
+<div id="{{ hero_slider_id }}" class="section-carousel carousel slide" data-ride="carousel">
{%- if show_indicators -%}
<ol class="carousel-indicators">
{%- for index in ['1', '2', '3', '4', '5'] -%}
{%- if values['slide_' + index + '_image'] -%}
- <li data-target="#{{ slider_name }}" data-slide-to="{{ frappe.utils.cint(index) - 1 }}" class="{{ 'active' if index=='1' else ''}}"></li>
+ <li data-target="#{{ hero_slider_id }}" data-slide-to="{{ frappe.utils.cint(index) - 1 }}" class="{{ 'active' if index=='1' else ''}}"></li>
{%- endif -%}
{%- endfor -%}
</ol>
@@ -54,7 +56,7 @@
{%- endfor -%}
</div>
{%- if show_controls -%}
- <a class="carousel-control-prev" href="#{{ slider_name }}" role="button" data-slide="prev">
+ <a class="carousel-control-prev" href="#{{ hero_slider_id }}" role="button" data-slide="prev">
<div class="carousel-control">
<svg class="mr-1" width="20" height="20" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.625 3.75L6.375 9L11.625 14.25" stroke="#4C5A67" stroke-linecap="round" stroke-linejoin="round"/>
@@ -62,7 +64,7 @@
</div>
<span class="sr-only">Previous</span>
</a>
- <a class="carousel-control-next" href="#{{ slider_name }}" role="button" data-slide="next">
+ <a class="carousel-control-next" href="#{{ hero_slider_id }}" role="button" data-slide="next">
<div class="carousel-control">
<svg class="ml-1" width="20" height="20" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.375 14.25L11.625 9L6.375 3.75" stroke="#4C5A67" stroke-linecap="round" stroke-linejoin="round"/>
@@ -73,13 +75,12 @@
{%- endif -%}
</div>
-<script type="text/javascript">
- $('.carousel').carousel({
- interval: false,
- pause: "hover",
- wrap: true
- })
+<script>
+ frappe.ready(function () {
+ $('.carousel').carousel({
+ interval: false,
+ pause: "hover",
+ wrap: true
+ })
+ });
</script>
-
-<style>
-</style>
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html
index 890ae50..fe061d5 100644
--- a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html
+++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html
@@ -35,4 +35,4 @@
</div>
<style>
-</style>
\ No newline at end of file
+</style>
diff --git a/erpnext/startup/__init__.py b/erpnext/startup/__init__.py
index deef4ba..3aa5297 100644
--- a/erpnext/startup/__init__.py
+++ b/erpnext/startup/__init__.py
@@ -19,7 +19,6 @@
# default settings that can be made for a user.
from __future__ import unicode_literals
-
product_name = "ERPNext"
user_defaults = {
"Company": "company",
diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py
index 2b80fb8..bb76f5a 100644
--- a/erpnext/startup/boot.py
+++ b/erpnext/startup/boot.py
@@ -3,9 +3,11 @@
from __future__ import unicode_literals
+
import frappe
from frappe.utils import cint
+
def boot_session(bootinfo):
"""boot session - send website info if guest"""
diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py
index ec07329..c0ccf54 100644
--- a/erpnext/startup/filters.py
+++ b/erpnext/startup/filters.py
@@ -1,5 +1,5 @@
-import frappe
+
def get_filters_config():
filters_config = {
@@ -11,4 +11,4 @@
}
}
- return filters_config
\ No newline at end of file
+ return filters_config
diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py
index 8819a55..60e67f8 100644
--- a/erpnext/startup/leaderboard.py
+++ b/erpnext/startup/leaderboard.py
@@ -1,8 +1,10 @@
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
+
import frappe
from frappe.utils import cint
+
def get_leaderboards():
leaderboards = {
"Customer": {
@@ -202,4 +204,4 @@
date_condition = "and {0} between {1} and {2}".format(
field, frappe.db.escape(from_date), frappe.db.escape(to_date)
)
- return date_condition
\ No newline at end of file
+ return date_condition
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index 8e880dc..01bb344 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+
def get_notification_config():
notifications = { "for_doctype":
{
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index 283f7d5..575aa0f 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
from frappe import _
diff --git a/erpnext/stock/dashboard/item_dashboard.html b/erpnext/stock/dashboard/item_dashboard.html
index 1e18969..99698ba 100644
--- a/erpnext/stock/dashboard/item_dashboard.html
+++ b/erpnext/stock/dashboard/item_dashboard.html
@@ -4,4 +4,4 @@
<div class="more hidden" style="padding: 15px;">
<a class="btn btn-default btn-xs btn-more">More</a>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py
index 45e6628..df6b632 100644
--- a/erpnext/stock/dashboard/item_dashboard.py
+++ b/erpnext/stock/dashboard/item_dashboard.py
@@ -2,7 +2,8 @@
import frappe
from frappe.model.db_query import DatabaseQuery
-from frappe.utils import flt, cint
+from frappe.utils import cint, flt
+
@frappe.whitelist()
def get_data(item_code=None, warehouse=None, item_group=None,
diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
index ab573e5..5d8b703 100644
--- a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
+++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
@@ -2,10 +2,11 @@
import frappe
from frappe.model.db_query import DatabaseQuery
-from frappe.utils import nowdate
-from frappe.utils import flt
+from frappe.utils import flt, nowdate
+
from erpnext.stock.utils import get_stock_balance
+
@frappe.whitelist()
def get_data(item_code=None, warehouse=None, parent_warehouse=None,
company=None, start=0, sort_by="stock_capacity", sort_order="desc"):
@@ -66,4 +67,4 @@
'percent_occupied': flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0)
})
- return capacity_data
\ No newline at end of file
+ return capacity_data
diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js
index a413754..2b9d46e 100644
--- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js
+++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js
@@ -11,4 +11,4 @@
default: frappe.defaults.get_user_default("Company")
}
]
-};
\ No newline at end of file
+};
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 374a34e..1753002 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
@@ -2,11 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, json
+
+import frappe
from frappe import _
from frappe.utils.dashboard import cache_source
+
from erpnext.stock.utils import get_stock_value_from_bin
+
@frappe.whitelist()
@cache_source
def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
@@ -45,4 +48,4 @@
"values": datapoints
}],
"type": "bar"
- }
\ No newline at end of file
+ }
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index b37ae3f..76db581 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -2,15 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-from six import text_type
+
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.naming import make_autoname, revert_series_if_last
-from frappe.utils import flt, cint, get_link_to_form
-from frappe.utils.jinja import render_template
+from frappe.utils import cint, flt, get_link_to_form
from frappe.utils.data import add_days
-from six import string_types
+from frappe.utils.jinja import render_template
+from six import text_type
+
class UnableToSelectBatchError(frappe.ValidationError):
pass
diff --git a/erpnext/stock/doctype/batch/test_batch.js b/erpnext/stock/doctype/batch/test_batch.js
index af7f50f..2d2150b 100644
--- a/erpnext/stock/doctype/batch/test_batch.js
+++ b/erpnext/stock/doctype/batch/test_batch.js
@@ -20,4 +20,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index a85a022..7998930 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -2,15 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.exceptions import ValidationError
import unittest
-from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
+import frappe
+from frappe.exceptions import ValidationError
from frappe.utils import cint, flt
+
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
from erpnext.stock.get_item_details import get_item_details
+
class TestBatch(unittest.TestCase):
def test_item_has_batch_enabled(self):
self.assertRaises(ValidationError, frappe.get_doc({
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 4364201..5fbc2d8 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -2,10 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import flt, nowdate
-import frappe.defaults
from frappe.model.document import Document
+from frappe.utils import flt, nowdate
+
class Bin(Document):
def before_save(self):
diff --git a/erpnext/stock/doctype/bin/test_bin.py b/erpnext/stock/doctype/bin/test_bin.py
index fb32ce2..f0dbe8c 100644
--- a/erpnext/stock/doctype/bin/test_bin.py
+++ b/erpnext/stock/doctype/bin/test_bin.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Bin')
diff --git a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.py b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.py
index b0b3e6a..d484301 100644
--- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.py
+++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class CustomsTariffNumber(Document):
pass
diff --git a/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.js b/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.js
deleted file mode 100644
index 85812d6..0000000
--- a/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Customs Tariff Number", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Customs Tariff Number
- () => frappe.tests.make('Customs Tariff Number', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.py b/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.py
index d7e0306..7c9807c 100644
--- a/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.py
+++ b/erpnext/stock/doctype/customs_tariff_number/test_customs_tariff_number.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestCustomsTariffNumber(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 36dfa6d..706ca36 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -356,3 +356,23 @@
dn_fields['taxes'].print_hide = 0;
}
}
+
+
+frappe.tour['Delivery Note'] = [
+ {
+ fieldname: "customer",
+ title: __("Customer"),
+ description: __("This field is used to set the 'Customer'.")
+ },
+ {
+ fieldname: "items",
+ title: __("Items"),
+ description: __("This table is used to set details about the 'Item', 'Qty', 'Basic Rate', etc.") + " " +
+ __("Different 'Source Warehouse' and 'Target Warehouse' can be set for each row.")
+ },
+ {
+ fieldname: "set_posting_time",
+ title: __("Edit Posting Date and Time"),
+ description: __("This option can be checked to edit the 'Posting Date' and 'Posting Time' fields.")
+ }
+]
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index dbfeb4a..ad1b3b4 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -395,8 +395,7 @@
"fieldtype": "Link",
"label": "Billing Address Name",
"options": "Address",
- "print_hide": 1,
- "read_only": 1
+ "print_hide": 1
},
{
"fieldname": "tax_id",
@@ -515,7 +514,8 @@
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -542,6 +542,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "packed_items",
+ "depends_on": "packed_items",
"fieldname": "packing_list",
"fieldtype": "Section Break",
"label": "Packing List",
@@ -550,6 +551,7 @@
"print_hide": 1
},
{
+ "depends_on": "packed_items",
"fieldname": "packed_items",
"fieldtype": "Table",
"label": "Packed Items",
@@ -1274,6 +1276,7 @@
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -1305,7 +1308,7 @@
"idx": 146,
"is_submittable": 1,
"links": [],
- "modified": "2021-07-08 21:37:20.802652",
+ "modified": "2021-10-08 14:29:13.428984",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index f99a01b..f75b52c 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -4,17 +4,17 @@
from __future__ import unicode_literals
import frappe
-import frappe.defaults
-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 frappe import _
from frappe.contacts.doctype.address.address import get_company_address
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
from frappe.utils import cint, flt
+
from erpnext.controllers.accounts_controller import get_taxes_and_charges
+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
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -185,7 +185,6 @@
if not d['warehouse'] and frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
-
def update_current_stock(self):
if self.get("_action") and self._action != "update_after_submit":
for d in self.get('items'):
@@ -331,7 +330,7 @@
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:
+ except Exception:
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):
@@ -668,8 +667,13 @@
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 (validate_inter_company_transaction,
- get_inter_company_details, update_address, update_taxes, set_purchase_references)
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+ get_inter_company_details,
+ set_purchase_references,
+ update_address,
+ update_taxes,
+ validate_inter_company_transaction,
+ )
if doctype == 'Delivery Note':
source_doc = frappe.get_doc(doctype, source_name)
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
index 47684d5..31fc708 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'delivery_note',
@@ -30,4 +32,4 @@
'items': ['Auto Repeat']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/stock/doctype/delivery_note/regional/india.js b/erpnext/stock/doctype/delivery_note/regional/india.js
index 5e1ff98..e853858 100644
--- a/erpnext/stock/doctype/delivery_note/regional/india.js
+++ b/erpnext/stock/doctype/delivery_note/regional/india.js
@@ -27,4 +27,3 @@
}
}
})
-
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.js b/erpnext/stock/doctype/delivery_note/test_delivery_note.js
index 3f6e8d1..76f7989 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.js
@@ -33,4 +33,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 756825e..7fda94b 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -3,26 +3,40 @@
from __future__ import unicode_literals
-import unittest
-import frappe
+
import json
-import frappe.defaults
-from frappe.utils import nowdate, nowtime, cstr, flt
-from erpnext.stock.stock_ledger import get_previous_sle
-from erpnext.accounts.utils import get_balance_on
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
-from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice, make_delivery_trip
-from erpnext.stock.doctype.stock_entry.test_stock_entry \
- import make_stock_entry, make_serialized_item, get_qty_after_transaction
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWarehouseError
-from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
- import create_stock_reconciliation, set_valuation_method
-from erpnext.selling.doctype.sales_order.test_sales_order \
- import make_sales_order, create_dn_against_so, automatically_fetch_payment_terms, compare_payment_schedules
+import unittest
+
+import frappe
+from frappe.utils import cstr, flt, nowdate, nowtime
+
from erpnext.accounts.doctype.account.test_account import get_inventory_account
-from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
-from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.accounts.utils import get_balance_on
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+from erpnext.selling.doctype.sales_order.test_sales_order import (
+ automatically_fetch_payment_terms,
+ compare_payment_schedules,
+ create_dn_against_so,
+ make_sales_order,
+)
+from erpnext.stock.doctype.delivery_note.delivery_note import (
+ make_delivery_trip,
+ make_sales_invoice,
+)
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
+from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError, get_serial_nos
+from erpnext.stock.doctype.stock_entry.test_stock_entry import (
+ get_qty_after_transaction,
+ make_serialized_item,
+ make_stock_entry,
+)
+from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+ create_stock_reconciliation,
+ set_valuation_method,
+)
+from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
+from erpnext.stock.stock_ledger import get_previous_sle
class TestDeliveryNote(unittest.TestCase):
@@ -430,12 +444,19 @@
})
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')
+ 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"
+ )
set_valuation_method("_Test Item", "FIFO")
set_valuation_method("_Test Item Home Desktop 100", "FIFO")
- target_warehouse=get_warehouse(company=company, abbr="TCP1",
+ target_warehouse = get_warehouse(company=company, abbr="TCP1",
warehouse_name="_Test Customer Warehouse").name
for warehouse in ("Stores - TCP1", target_warehouse):
@@ -444,10 +465,16 @@
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', cost_center = 'Main - TCP1',
- expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True, qty=5, rate=500,
- warehouse="Stores - TCP1", target_warehouse=target_warehouse)
+ 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,
+ warehouse="Stores - TCP1",
+ target_warehouse=target_warehouse)
dn.submit()
@@ -487,6 +514,9 @@
for i, gle in enumerate(gl_entries):
self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
+ # tear down
+ frappe.db.rollback()
+
def test_closed_delivery_note(self):
from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
@@ -524,7 +554,10 @@
def test_dn_billing_status_case2(self):
# SO -> SI and SO -> DN1, DN2
- from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice
+ from erpnext.selling.doctype.sales_order.sales_order import (
+ make_delivery_note,
+ make_sales_invoice,
+ )
so = make_sales_order()
@@ -563,8 +596,10 @@
def test_dn_billing_status_case3(self):
# SO -> DN1 -> SI and SO -> SI and SO -> DN2
- from erpnext.selling.doctype.sales_order.sales_order \
- import make_delivery_note, make_sales_invoice as make_sales_invoice_from_so
+ from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
+ 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()
@@ -610,8 +645,8 @@
def test_dn_billing_status_case4(self):
# SO -> SI -> DN
- from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
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()
@@ -761,7 +796,9 @@
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 create_payment_terms_template
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
+ create_payment_terms_template,
+ )
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
automatically_fetch_payment_terms()
@@ -772,7 +809,7 @@
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].dn_detail = dn.items[0].name
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js b/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js
index 21eb35c..9f1375f 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js
@@ -34,4 +34,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index b05090a..a96c299 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -468,7 +468,7 @@
"width": "100px"
},
{
- "depends_on": "eval:parent.is_internal_customer",
+ "depends_on": "eval:parent.is_internal_customer || doc.target_warehouse",
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 1,
@@ -759,7 +759,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-02-23 01:04:08.588104",
+ "modified": "2021-10-05 12:12:44.018872",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Item",
@@ -767,4 +767,4 @@
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
index 5030595..693caab 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class DeliveryNoteItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/delivery_settings/delivery_settings.py b/erpnext/stock/doctype/delivery_settings/delivery_settings.py
index 909efda..c25907d 100644
--- a/erpnext/stock/doctype/delivery_settings/delivery_settings.py
+++ b/erpnext/stock/doctype/delivery_settings/delivery_settings.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class DeliverySettings(Document):
pass
diff --git a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.js b/erpnext/stock/doctype/delivery_settings/test_delivery_settings.js
deleted file mode 100644
index 22977c0..0000000
--- a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Delivery Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Delivery Settings
- () => frappe.tests.make('Delivery Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.py b/erpnext/stock/doctype/delivery_settings/test_delivery_settings.py
index 4395d26..25c9da1 100644
--- a/erpnext/stock/doctype/delivery_settings/test_delivery_settings.py
+++ b/erpnext/stock/doctype/delivery_settings/test_delivery_settings.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestDeliverySettings(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/delivery_stop/delivery_stop.py b/erpnext/stock/doctype/delivery_stop/delivery_stop.py
index 768d161..f94ccb8 100644
--- a/erpnext/stock/doctype/delivery_stop/delivery_stop.py
+++ b/erpnext/stock/doctype/delivery_stop/delivery_stop.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class DeliveryStop(Document):
pass
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index 9ec28d8..fe98182 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -10,8 +10,8 @@
from frappe import _
from frappe.contacts.doctype.address.address import get_address_display
from frappe.model.document import Document
-from frappe.utils import cint, get_datetime, get_link_to_form
from frappe.model.mapper import get_mapped_doc
+from frappe.utils import cint, get_datetime, get_link_to_form
class DeliveryTrip(Document):
@@ -406,4 +406,4 @@
}
}}, target_doc)
- return doc
\ No newline at end of file
+ return doc
diff --git a/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html b/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html
index 9c062bc..d12334e 100644
--- a/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html
+++ b/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html
@@ -47,4 +47,4 @@
<td>{{ vehicle }}</td>
</tr>
</tbody>
-</table>
\ No newline at end of file
+</table>
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.js b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.js
deleted file mode 100644
index b6d6d1a..0000000
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Delivery Trip", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Delivery Trip
- () => frappe.tests.make('Delivery Trip', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index 1e71603..c9081c9 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -5,12 +5,17 @@
import unittest
-import erpnext
import frappe
-from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers, make_expense_claim
-from erpnext.tests.utils import create_test_contact_and_address
from frappe.utils import add_days, flt, now_datetime, nowdate
+import erpnext
+from erpnext.stock.doctype.delivery_trip.delivery_trip import (
+ get_contact_and_address,
+ make_expense_claim,
+ notify_customers,
+)
+from erpnext.tests.utils import create_test_contact_and_address
+
class TestDeliveryTrip(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index c5bc9f1..752a1fe 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -141,9 +141,8 @@
is_fixed_asset: function(frm) {
// set serial no to false & toggles its visibility
frm.set_value('has_serial_no', 0);
+ frm.set_value('has_batch_no', 0);
frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
- frm.toggle_reqd(['asset_category'], frm.doc.is_fixed_asset);
- frm.toggle_display(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
frm.call({
method: "set_asset_naming_series",
@@ -792,4 +791,49 @@
});
}
}
-})
+});
+
+frappe.tour['Item'] = [
+ {
+ fieldname: "item_code",
+ title: "Item Code",
+ description: __("Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field.")
+ },
+ {
+ fieldname: "item_group",
+ title: "Item Group",
+ description: __("Select an Item Group.")
+ },
+ {
+ fieldname: "is_stock_item",
+ title: "Maintain Stock",
+ description: __("If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item.")
+ },
+ {
+ fieldname: "include_item_in_manufacturing",
+ title: "Include Item in Manufacturing",
+ description: __("This is for raw material Items that'll be used to create finished goods. If the Item is an additional service like 'washing' that'll be used in the BOM, keep this unchecked.")
+ },
+ {
+ fieldname: "opening_stock",
+ title: "Opening Stock",
+ description: __("Enter the opening stock units.")
+ },
+ {
+ fieldname: "valuation_rate",
+ title: "Valuation Rate",
+ description: __("There are two options to maintain valuation of stock. FIFO (first in - first out) and Moving Average. To understand this topic in detail please visit <a href='https://docs.erpnext.com/docs/v13/user/manual/en/stock/articles/item-valuation-fifo-and-moving-average' target='_blank'>Item Valuation, FIFO and Moving Average.</a>")
+ },
+ {
+ fieldname: "standard_rate",
+ title: "Standard Selling Rate",
+ description: __("When creating an Item, entering a value for this field will automatically create an Item Price at the backend.")
+ },
+ {
+ fieldname: "item_defaults",
+ title: "Item Defaults",
+ description: __("In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc.")
+ }
+
+
+];
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index f662bbd..db5caf9 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -204,6 +204,7 @@
},
{
"default": "0",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "is_item_from_hub",
"fieldtype": "Check",
"label": "Is Item from Hub",
@@ -238,6 +239,7 @@
{
"bold": 1,
"default": "1",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "is_stock_item",
"fieldtype": "Check",
"label": "Maintain Stock",
@@ -246,6 +248,7 @@
},
{
"default": "1",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
"label": "Include Item In Manufacturing"
@@ -282,6 +285,7 @@
"fieldname": "asset_category",
"fieldtype": "Link",
"label": "Asset Category",
+ "mandatory_depends_on": "is_fixed_asset",
"options": "Asset Category"
},
{
@@ -434,8 +438,8 @@
},
{
"collapsible": 1,
- "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no || doc.is_fixed_asset",
- "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+ "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no",
+ "depends_on": "eval:doc.is_stock_item",
"fieldname": "serial_nos_and_batches",
"fieldtype": "Section Break",
"label": "Serial Nos and Batches"
@@ -492,7 +496,7 @@
},
{
"default": "0",
- "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+ "depends_on": "eval:doc.is_stock_item",
"fieldname": "has_serial_no",
"fieldtype": "Check",
"label": "Has Serial No",
@@ -510,6 +514,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "attributes",
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "variants_section",
"fieldtype": "Section Break",
"label": "Variants"
@@ -540,6 +545,7 @@
"options": "Item Variant Attribute"
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "defaults",
"fieldtype": "Section Break",
"label": "Sales, Purchase, Accounting Defaults"
@@ -621,6 +627,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "supplier_details",
"fieldtype": "Section Break",
"label": "Supplier Details"
@@ -668,6 +675,7 @@
},
{
"collapsible": 1,
+ "default": "eval:!doc.is_fixed_asset",
"fieldname": "sales_details",
"fieldtype": "Section Break",
"label": "Sales Details",
@@ -761,6 +769,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "customer_details",
"fieldtype": "Section Break",
"label": "Customer Details"
@@ -791,6 +800,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "inspection_criteria",
"fieldtype": "Section Break",
"label": "Inspection Criteria",
@@ -861,6 +871,7 @@
},
{
"collapsible": 1,
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "website_section",
"fieldtype": "Section Break",
"label": "Website",
@@ -987,7 +998,7 @@
},
{
"collapsible": 1,
- "depends_on": "eval:(!doc.is_item_from_hub)",
+ "depends_on": "eval:(!doc.is_item_from_hub && !doc.is_fixed_asset)",
"fieldname": "hub_publishing_sb",
"fieldtype": "Section Break",
"label": "Hub Publishing Details"
@@ -1021,7 +1032,7 @@
"read_only": 1
},
{
- "depends_on": "eval:!doc.__islocal",
+ "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset",
"fieldname": "over_delivery_receipt_allowance",
"fieldtype": "Float",
"label": "Over Delivery/Receipt Allowance (%)",
@@ -1029,7 +1040,7 @@
"oldfieldtype": "Currency"
},
{
- "depends_on": "eval:!doc.__islocal",
+ "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset",
"fieldname": "over_billing_allowance",
"fieldtype": "Float",
"label": "Over Billing Allowance (%)"
@@ -1067,7 +1078,7 @@
"index_web_pages_for_search": 1,
"links": [],
"max_attachments": 1,
- "modified": "2021-07-13 01:29:06.071827",
+ "modified": "2021-08-26 12:23:07.277077",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 614c53a..8cc9f74 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -1,24 +1,44 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
+import copy
import itertools
import json
-import erpnext
-import frappe
-import copy
-from erpnext.controllers.item_variant import (ItemVariantExistsError,
- copy_attributes_to_variant, get_variant, make_variant_item_code, validate_item_variant_attributes)
-from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for)
-from frappe import _, msgprint
-from frappe.utils import (cint, cstr, flt, formatdate, getdate,
- now_datetime, random_string, strip, get_link_to_form, nowtime)
-from frappe.utils.html_utils import clean_html
-from frappe.website.doctype.website_slideshow.website_slideshow import \
- get_slideshow
+from typing import List
+import frappe
+from frappe import _
+from frappe.utils import (
+ cint,
+ cstr,
+ flt,
+ formatdate,
+ get_link_to_form,
+ getdate,
+ now_datetime,
+ nowtime,
+ random_string,
+ strip,
+)
+from frappe.utils.html_utils import clean_html
+from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
from frappe.website.utils import clear_cache
from frappe.website.website_generator import WebsiteGenerator
+import erpnext
+from erpnext.controllers.item_variant import (
+ ItemVariantExistsError,
+ copy_attributes_to_variant,
+ get_variant,
+ make_variant_item_code,
+ validate_item_variant_attributes,
+)
+from erpnext.setup.doctype.item_group.item_group import (
+ get_parent_item_groups,
+ invalidate_cache_for,
+)
+from erpnext.stock.doctype.item_default.item_default import ItemDefault
+
class DuplicateReorderRows(frappe.ValidationError):
pass
@@ -116,9 +136,9 @@
self.validate_fixed_asset()
self.validate_retain_sample()
self.validate_uom_conversion_factor()
- self.validate_item_defaults()
self.validate_customer_provided_part()
self.update_defaults_from_item_group()
+ self.validate_item_defaults()
self.validate_auto_reorder_enabled_in_stock_settings()
self.cant_change()
self.update_show_in_website()
@@ -161,6 +181,8 @@
"doctype": "Item Price",
"price_list": price_list,
"item_code": self.name,
+ "uom": self.stock_uom,
+ "brand": self.brand,
"currency": erpnext.get_default_currency(),
"price_list_rate": self.standard_rate
})
@@ -543,8 +565,12 @@
_("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 """
- self.customer_code = ','.join(d.ref_code for d in self.get("customer_items", []))
+ """
+ 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)
def check_item_tax(self):
"""Check whether Tax Rate is not entered twice for same Tax Type"""
@@ -610,9 +636,21 @@
_("An Item Group exists with same name, please change the item name or rename the item group"))
def update_item_price(self):
- frappe.db.sql("""update `tabItem Price` set item_name=%s,
- item_description=%s, brand=%s where item_code=%s""",
- (self.item_name, self.description, self.brand, self.name))
+ frappe.db.sql("""
+ UPDATE `tabItem Price`
+ SET
+ item_name=%(item_name)s,
+ item_description=%(item_description)s,
+ brand=%(brand)s
+ WHERE item_code=%(item_code)s
+ """,
+ dict(
+ item_name=self.item_name,
+ item_description=self.description,
+ brand=self.brand,
+ item_code=self.name
+ )
+ )
def on_trash(self):
super(Item, self).on_trash()
@@ -764,35 +802,39 @@
if len(companies) != len(self.item_defaults):
frappe.throw(_("Cannot set multiple Item Defaults for a company."))
+ validate_item_default_company_links(self.item_defaults)
+
+
def update_defaults_from_item_group(self):
"""Get defaults from Item Group"""
- if self.item_group and not self.item_defaults:
- 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
- })
- else:
- warehouse = ''
- defaults = frappe.defaults.get_defaults() or {}
+ if self.item_defaults or not self.item_group:
+ return
- # 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
- })
+ 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
+ })
+ 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
+ })
def update_variants(self):
if self.flags.dont_update_variants or \
@@ -1309,4 +1351,26 @@
@erpnext.allow_regional
def set_item_tax_from_hsn_code(item):
- pass
\ No newline at end of file
+ pass
+
+
+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']
+ ]:
+ if item_default.get(field):
+ 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(
+ item_default.idx,
+ doctype,
+ frappe.bold(item_default.get(field)),
+ frappe.bold(item_default.company),
+ frappe.bold(frappe.unscrub(field))
+ ), title=_("Invalid Item Defaults"))
diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py
index b3e4796..e80ed6f 100644
--- a/erpnext/stock/doctype/item/item_dashboard.py
+++ b/erpnext/stock/doctype/item/item_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'heatmap': True,
diff --git a/erpnext/stock/doctype/item/regional/india.js b/erpnext/stock/doctype/item/regional/india.js
index 77ae51f..cceb1ec 100644
--- a/erpnext/stock/doctype/item/regional/india.js
+++ b/erpnext/stock/doctype/item/regional/india.js
@@ -12,4 +12,4 @@
});
}
},
-});
\ No newline at end of file
+});
diff --git a/erpnext/stock/doctype/item/templates/item.html b/erpnext/stock/doctype/item/templates/item.html
index db12309..5c42f3b 100644
--- a/erpnext/stock/doctype/item/templates/item.html
+++ b/erpnext/stock/doctype/item/templates/item.html
@@ -4,4 +4,4 @@
<h1>{{ title }}</h1>
{% endblock %}
-<!-- this is a sample default web page template -->
\ No newline at end of file
+<!-- this is a sample default web page template -->
diff --git a/erpnext/stock/doctype/item/templates/item_row.html b/erpnext/stock/doctype/item/templates/item_row.html
index 2b99981..f81fc1d 100644
--- a/erpnext/stock/doctype/item/templates/item_row.html
+++ b/erpnext/stock/doctype/item/templates/item_row.html
@@ -1,4 +1,4 @@
<div>
<a href={{ route }}>{{ title }}</a>
</div>
-<!-- this is a sample default list template -->
\ No newline at end of file
+<!-- this is a sample default list template -->
diff --git a/erpnext/stock/doctype/item/test_item.js b/erpnext/stock/doctype/item/test_item.js
deleted file mode 100644
index af44278..0000000
--- a/erpnext/stock/doctype/item/test_item.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Item
- () => frappe.tests.make('Item', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 7a9985d..e911d35 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -2,21 +2,31 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import unittest
-import frappe
-import json
+import json
+import unittest
+
+import frappe
from frappe.test_runner import make_test_objects
-from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError,
- InvalidItemAttributeValueError, get_variant)
-from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode
-from erpnext.stock.doctype.item.item import (get_uom_conv_factor, get_item_attribute,
- validate_is_stock_item, get_timeline_data)
+
+from erpnext.controllers.item_variant import (
+ InvalidItemAttributeValueError,
+ ItemVariantExistsError,
+ create_variant,
+ get_variant,
+)
+from erpnext.stock.doctype.item.item import (
+ InvalidBarcode,
+ StockExistsForTemplate,
+ get_item_attribute,
+ get_timeline_data,
+ get_uom_conv_factor,
+ validate_is_stock_item,
+)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.get_item_details import get_item_details
from erpnext.tests.utils import change_settings
-
test_ignore = ["BOM"]
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -222,6 +232,23 @@
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",
+ }]
+ })
+
+ 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)
diff --git a/erpnext/stock/doctype/item/tests/test_item.js b/erpnext/stock/doctype/item/tests/test_item.js
index 5e3524e..7f7e72d 100644
--- a/erpnext/stock/doctype/item/tests/test_item.js
+++ b/erpnext/stock/doctype/item/tests/test_item.js
@@ -118,4 +118,4 @@
),
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py
index 190cb62..6080fb4 100644
--- a/erpnext/stock/doctype/item_alternative/item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/item_alternative.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
class ItemAlternative(Document):
def validate(self):
self.has_alternative_item()
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.js b/erpnext/stock/doctype/item_alternative/test_item_alternative.js
deleted file mode 100644
index 8731849..0000000
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Alternative", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Item Alternative
- () => frappe.tests.make('Item Alternative', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index 8f76844..2be8ef7 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -2,17 +2,27 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.utils import flt
-from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
-from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
-from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
-from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry
+
+import json
import unittest
+import frappe
+from frappe.utils import flt
+
+from erpnext.buying.doctype.purchase_order.purchase_order import (
+ make_purchase_receipt,
+ make_rm_stock_entry,
+)
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+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.test_stock_reconciliation import (
+ create_stock_reconciliation,
+)
+
+
class TestItemAlternative(unittest.TestCase):
def setUp(self):
make_items()
diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py
index 3764738..9894788 100644
--- a/erpnext/stock/doctype/item_attribute/item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/item_attribute.py
@@ -2,13 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
from frappe.utils import flt
-from erpnext.controllers.item_variant import (validate_is_incremental,
- validate_item_attribute_value, InvalidItemAttributeValueError)
+from erpnext.controllers.item_variant import (
+ InvalidItemAttributeValueError,
+ validate_is_incremental,
+ validate_item_attribute_value,
+)
class ItemAttributeIncrementError(frappe.ValidationError): pass
diff --git a/erpnext/stock/doctype/item_attribute/test_item_attribute.py b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
index 61e53d2..fc809f4 100644
--- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
@@ -2,13 +2,16 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
+import frappe
+
test_records = frappe.get_test_records('Item Attribute')
from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
+
class TestItemAttribute(unittest.TestCase):
def setUp(self):
if frappe.db.exists("Item Attribute", "_Test_Length"):
@@ -28,4 +31,3 @@
item_attribute.increment = 0.5
item_attribute.save()
-
diff --git a/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py b/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py
index edbab00..ceffb49 100644
--- a/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py
+++ b/erpnext/stock/doctype/item_attribute_value/item_attribute_value.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ItemAttributeValue(Document):
pass
diff --git a/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py b/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py
index a9183ce..55fd0ec 100644
--- a/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py
+++ b/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ItemCustomerDetail(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/item_default/item_default.py b/erpnext/stock/doctype/item_default/item_default.py
index 935f0ff..6239c54 100644
--- a/erpnext/stock/doctype/item_default/item_default.py
+++ b/erpnext/stock/doctype/item_default/item_default.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class ItemDefault(Document):
pass
diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
index c27d1be..044ac7c 100644
--- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
+++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py
@@ -8,6 +8,7 @@
from frappe import _
from frappe.model.document import Document
+
class ItemManufacturer(Document):
def validate(self):
self.validate_duplicate_entry()
@@ -65,4 +66,4 @@
@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')
\ No newline at end of file
+ {'item_code': item_code, 'manufacturer': manufacturer}, 'manufacturer_part_no')
diff --git a/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
index 1cef20c..5a4ca6a 100644
--- a/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
+++ b/erpnext/stock/doctype/item_manufacturer/test_item_manufacturer.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestItemManufacturer(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index e82a19b..3f0fc41 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index f3d406e..5ed8092 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -2,11 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import unittest
+
import frappe
from frappe.test_runner import make_test_records_for_doctype
-from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
+
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
+from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
class TestItemPrice(unittest.TestCase):
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py
index 92aefc8..0dd7e43 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ItemQualityInspectionParameter(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/item_reorder/item_reorder.py b/erpnext/stock/doctype/item_reorder/item_reorder.py
index 0f9c593..598339d 100644
--- a/erpnext/stock/doctype/item_reorder/item_reorder.py
+++ b/erpnext/stock/doctype/item_reorder/item_reorder.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ItemReorder(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/item_supplier/item_supplier.py b/erpnext/stock/doctype/item_supplier/item_supplier.py
index 1a07f03..9b5da55 100644
--- a/erpnext/stock/doctype/item_supplier/item_supplier.py
+++ b/erpnext/stock/doctype/item_supplier/item_supplier.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ItemSupplier(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/item_tax/item_tax.py b/erpnext/stock/doctype/item_tax/item_tax.py
index 1fe2f45..33c1e49 100644
--- a/erpnext/stock/doctype/item_tax/item_tax.py
+++ b/erpnext/stock/doctype/item_tax/item_tax.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ItemTax(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/item_variant/item_variant.py b/erpnext/stock/doctype/item_variant/item_variant.py
index 5d5a022..47ab07f 100644
--- a/erpnext/stock/doctype/item_variant/item_variant.py
+++ b/erpnext/stock/doctype/item_variant/item_variant.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ItemVariant(Document):
pass
diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py
index d1a1eb5..78dda65 100644
--- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py
+++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class ItemVariantAttribute(Document):
pass
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 e8fb347..488920a 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
@@ -2,19 +2,32 @@
// For license information, please see license.txt
frappe.ui.form.on('Item Variant Settings', {
- setup: function(frm) {
+ refresh: function(frm) {
const allow_fields = [];
- const exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
- "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"];
+
+ const existing_fields = frm.doc.fields.map(row => row.field_name);
+ const exclude_fields = [...existing_fields, "naming_series", "item_code", "item_name",
+ "show_in_website", "show_variant_in_website", "standard_rate", "opening_stock", "image",
+ "variant_of", "valuation_rate", "barcodes", "website_image", "thumbnail",
+ "website_specifiations", "web_long_description", "has_variants", "attributes"];
+
+ const exclude_field_types = ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only'];
frappe.model.with_doctype('Item', () => {
frappe.get_meta('Item').fields.forEach(d => {
- if(!in_list(['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only'], d.fieldtype)
+ if (!in_list(exclude_field_types, d.fieldtype)
&& !d.no_copy && !in_list(exclude_fields, d.fieldname)) {
allow_fields.push(d.fieldname);
}
});
+ if (allow_fields.length == 0) {
+ allow_fields.push({
+ label: __("No additional fields available"),
+ value: "",
+ });
+ }
+
frm.fields_dict.fields.grid.update_docfield_property(
'field_name', 'options', allow_fields
);
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 78f1131..cb6626f 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
from frappe import _
+from frappe.model.document import Document
+
class ItemVariantSettings(Document):
invalid_fields_for_copy_fields_in_variants = ['barcodes']
diff --git a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js
deleted file mode 100644
index 3b3bf94..0000000
--- a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Item Variant Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Item Variant Settings
- () => frappe.tests.make('Item Variant Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py
index 9a800c0..040382a 100644
--- a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestItemVariantSettings(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/item_website_specification/item_website_specification.py b/erpnext/stock/doctype/item_website_specification/item_website_specification.py
index 6d0dbad..85491b7 100644
--- a/erpnext/stock/doctype/item_website_specification/item_website_specification.py
+++ b/erpnext/stock/doctype/item_website_specification/item_website_specification.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class ItemWebsiteSpecification(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py
index 0521a7a..7dd3aa5 100644
--- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py
+++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class LandedCostItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py
index f7ccb9b..3d81d96 100644
--- a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py
+++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class LandedCostPurchaseReceipt(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py
index e445820..e649e4d 100644
--- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py
+++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class LandedCostTaxesandCharges(Document):
- pass
\ No newline at end of file
+ pass
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 bf969f9..51ccea9 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -2,14 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from frappe.utils import flt
-from frappe.model.meta import get_field_precision
from frappe.model.document import Document
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-from erpnext.accounts.doctype.account.account import get_account_currency
+from frappe.model.meta import get_field_precision
+from frappe.utils import flt
+
+import erpnext
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
class LandedCostVoucher(Document):
@frappe.whitelist()
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 cb09d93..58a72f7 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
@@ -3,15 +3,20 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
from frappe.utils import flt
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
- import get_gl_entries, test_records as pr_test_records, make_purchase_receipt
+
+from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
-from erpnext.accounts.doctype.account.test_account import get_inventory_account
-from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.assets.doctype.asset.test_asset import create_asset_category, create_fixed_asset_item
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import (
+ get_gl_entries,
+ make_purchase_receipt,
+)
+
class TestLandedCostVoucher(unittest.TestCase):
def test_landed_cost_voucher(self):
@@ -208,7 +213,10 @@
self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100)
def test_multi_currency_lcv(self):
- from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
+ from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
+ save_new_records,
+ test_records,
+ )
save_new_records(test_records)
diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.py b/erpnext/stock/doctype/manufacturer/manufacturer.py
index b624f73..314a280 100644
--- a/erpnext/stock/doctype/manufacturer/manufacturer.py
+++ b/erpnext/stock/doctype/manufacturer/manufacturer.py
@@ -3,10 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+
+from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.model.document import Document
+
class Manufacturer(Document):
def onload(self):
"""Load address and contacts in `__onload`"""
diff --git a/erpnext/stock/doctype/manufacturer/test_manufacturer.js b/erpnext/stock/doctype/manufacturer/test_manufacturer.js
deleted file mode 100644
index 0254a36..0000000
--- a/erpnext/stock/doctype/manufacturer/test_manufacturer.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Manufacturer", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Manufacturer', [
- // insert a new Manufacturer
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/manufacturer/test_manufacturer.py b/erpnext/stock/doctype/manufacturer/test_manufacturer.py
index 996f6b2..c0c61b0 100644
--- a/erpnext/stock/doctype/manufacturer/test_manufacturer.py
+++ b/erpnext/stock/doctype/manufacturer/test_manufacturer.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Manufacturer')
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index 4e2d9e6..cb46a6c 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -133,7 +133,8 @@
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "options": "Barcode"
},
{
"allow_bulk_edit": 1,
@@ -181,7 +182,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nPartially Received\nOrdered\nIssued\nTransferred\nReceived",
+ "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nOrdered\nIssued\nTransferred\nReceived",
"print_hide": 1,
"print_width": "100px",
"read_only": 1,
@@ -314,7 +315,7 @@
"idx": 70,
"is_submittable": 1,
"links": [],
- "modified": "2021-03-31 23:52:55.392512",
+ "modified": "2021-08-17 20:16:12.737743",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 026b85e..17df977 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -5,19 +5,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from frappe.utils import cstr, flt, getdate, new_line_sep, nowdate, add_days, get_link_to_form
-from frappe import msgprint, _
+import frappe
+from frappe import _, msgprint
from frappe.model.mapper import get_mapped_doc
-from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
+from frappe.utils import cstr, flt, get_link_to_form, getdate, new_line_sep, nowdate
+from six import string_types
+
+from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
from erpnext.controllers.buying_controller import BuyingController
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
-from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
from erpnext.stock.doctype.item.item import get_item_defaults
-
-from six import string_types
+from erpnext.stock.stock_balance import get_indented_qty, update_bin_qty
form_grid_templates = {
"items": "templates/form_grid/material_request_grid.html"
@@ -271,7 +272,11 @@
material_request.update_status(status)
@frappe.whitelist()
-def make_purchase_order(source_name, target_doc=None):
+def make_purchase_order(source_name, target_doc=None, args=None):
+ if args is None:
+ args = {}
+ if isinstance(args, string_types):
+ args = json.loads(args)
def postprocess(source, target_doc):
if frappe.flags.args and frappe.flags.args.default_supplier:
@@ -286,9 +291,12 @@
set_missing_values(source, target_doc)
def select_item(d):
- return d.ordered_qty < d.stock_qty
+ filtered_items = args.get('filtered_children', [])
+ child_filter = d.name in filtered_items if filtered_items else True
- doclist = get_mapped_doc("Material Request", source_name, {
+ return d.ordered_qty < d.stock_qty and child_filter
+
+ doclist = get_mapped_doc("Material Request", source_name, {
"Material Request": {
"doctype": "Purchase Order",
"validation": {
@@ -315,7 +323,7 @@
@frappe.whitelist()
def make_request_for_quotation(source_name, target_doc=None):
- doclist = get_mapped_doc("Material Request", source_name, {
+ doclist = get_mapped_doc("Material Request", source_name, {
"Material Request": {
"doctype": "Request for Quotation",
"validation": {
diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py
index f3e5e5d..291cfb5 100644
--- a/erpnext/stock/doctype/material_request/material_request_dashboard.py
+++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
from frappe import _
@@ -20,4 +21,4 @@
'items': ['Work Order']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/stock/doctype/material_request/test_material_request.js b/erpnext/stock/doctype/material_request/test_material_request.js
deleted file mode 100644
index 793cad0..0000000
--- a/erpnext/stock/doctype/material_request/test_material_request.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Material Request", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially('Material Request', [
- // insert a new Material Request
- () => frappe.tests.make([
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index b4776ba..5c2ac25 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -5,11 +5,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, unittest, erpnext
+
+import unittest
+
+import frappe
from frappe.utils import flt, today
-from erpnext.stock.doctype.material_request.material_request \
- import raise_work_orders, make_stock_entry, make_purchase_order, make_supplier_quotation
+
from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.material_request.material_request import (
+ make_purchase_order,
+ make_stock_entry,
+ make_supplier_quotation,
+ raise_work_orders,
+)
+
class TestMaterialRequest(unittest.TestCase):
def test_make_purchase_order(self):
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request.js b/erpnext/stock/doctype/material_request/tests/test_material_request.js
index bf26cd1..a2cd03b 100644
--- a/erpnext/stock/doctype/material_request/tests/test_material_request.js
+++ b/erpnext/stock/doctype/material_request/tests/test_material_request.js
@@ -37,4 +37,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js
index d8b39fe..6fb55ae 100644
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js
+++ b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js
@@ -25,4 +25,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js
index 91b47ba..137079b 100644
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js
+++ b/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js
@@ -27,4 +27,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js
index 050e0f0..b03a854 100644
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js
+++ b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js
@@ -27,4 +27,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js
index d6f9b66..7c62c2e 100644
--- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js
+++ b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js
@@ -27,4 +27,3 @@
() => done()
]);
});
-
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 16f007f..0c98b97 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.py
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.py
@@ -4,12 +4,13 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe.model.document import Document
+
class MaterialRequestItem(Document):
pass
def on_doctype_update():
- frappe.db.add_index("Material Request Item", ["item_code", "warehouse"])
\ No newline at end of file
+ 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 bb396e8..830d546 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -16,6 +16,7 @@
"conversion_factor",
"column_break_9",
"qty",
+ "rate",
"uom",
"section_break_9",
"serial_no",
@@ -215,13 +216,23 @@
"fieldname": "conversion_factor",
"fieldtype": "Float",
"label": "Conversion Factor"
+ },
+ {
+ "fetch_from": "item_code.valuation_rate",
+ "fetch_if_empty": 1,
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Rate",
+ "print_hide": 1,
+ "read_only": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-05-26 07:08:05.111385",
+ "modified": "2021-09-01 15:10:29.646399",
"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 4ab71bd..08a2447 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -4,10 +4,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe.utils import cstr, flt
-from erpnext.stock.get_item_details import get_item_details
+
+import json
+
+import frappe
from frappe.model.document import Document
+from frappe.utils import cstr, flt
+
+from erpnext.stock.get_item_details import get_item_details
+
class PackedItem(Document):
pass
@@ -39,8 +44,10 @@
# check if exists
exists = 0
for d in doc.get("packed_items"):
- if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code and\
- d.parent_detail_docname == main_item_row.name:
+ if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code:
+ if d.parent_detail_docname != main_item_row.name:
+ d.parent_detail_docname = main_item_row.name
+
pi, exists = d, 1
break
@@ -86,6 +93,9 @@
cleanup_packing_list(doc, parent_items)
+ if frappe.db.get_single_value("Selling Settings", "editable_bundle_item_rates"):
+ update_product_bundle_price(doc, parent_items)
+
def cleanup_packing_list(doc, parent_items):
"""Remove all those child items which are no longer present in main item table"""
delete_list = []
@@ -103,6 +113,40 @@
if d not in delete_list:
doc.append("packed_items", d)
+def update_product_bundle_price(doc, parent_items):
+ """Updates the prices of Product Bundles based on the rates of the Items in the bundle."""
+
+ if not doc.get('items'):
+ return
+
+ parent_items_index = 0
+ bundle_price = 0
+
+ for bundle_item in doc.get("packed_items"):
+ if parent_items[parent_items_index][0] == bundle_item.parent_item:
+ bundle_item_rate = bundle_item.rate if bundle_item.rate else 0
+ bundle_price += bundle_item.qty * bundle_item_rate
+ else:
+ update_parent_item_price(doc, parent_items[parent_items_index][0], bundle_price)
+
+ bundle_price = 0
+ parent_items_index += 1
+
+ # for the last product bundle
+ if doc.get("packed_items"):
+ update_parent_item_price(doc, parent_items[parent_items_index][0], bundle_price)
+
+def update_parent_item_price(doc, parent_item_code, bundle_price):
+ parent_item_doc = doc.get('items', {'item_code': parent_item_code})[0]
+
+ current_parent_item_price = parent_item_doc.amount
+ if current_parent_item_price != bundle_price:
+ parent_item_doc.amount = bundle_price
+ update_parent_item_rate(parent_item_doc, bundle_price)
+
+def update_parent_item_rate(parent_item_doc, bundle_price):
+ parent_item_doc.rate = bundle_price/parent_item_doc.qty
+
@frappe.whitelist()
def get_items_from_product_bundle(args):
args = json.loads(args)
diff --git a/erpnext/stock/doctype/packing_slip/test_packing_slip.py b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
index 1f2af02..193adfc 100644
--- a/erpnext/stock/doctype/packing_slip/test_packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('Packing Slip')
diff --git a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py
index 694ab38..8363968 100644
--- a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py
+++ b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py
@@ -4,9 +4,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PackingSlipItem(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index ee218f2..730fd7a 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -201,4 +201,4 @@
uom
});
}
-}
\ 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 516ae43..dffbe80 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -3,16 +3,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from six import iteritems
-from frappe.model.document import Document
-from frappe import _
from collections import OrderedDict
-from frappe.utils import floor, flt, today, cint
-from frappe.model.mapper import get_mapped_doc, map_child_doc
+
+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 six import iteritems
+
+from erpnext.selling.doctype.sales_order.sales_order import (
+ make_delivery_note as create_delivery_note_from_sales_order,
+)
from erpnext.stock.get_item_details import get_conversion_factor
-from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order
# TODO: Prioritize SO or WO group warehouse
diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
index 6e007df..50a767b 100644
--- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
+++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-from frappe import _
+
def get_data():
return {
@@ -9,4 +9,4 @@
'items': ['Stock Entry', 'Delivery Note']
},
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 84566b8..aa710ad 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -3,15 +3,19 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
+import frappe
+
test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
-from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \
- import EmptyStockReconciliationItemsError
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
+ EmptyStockReconciliationItemsError,
+)
+
class TestPickList(unittest.TestCase):
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 8665986..805286d 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
@@ -36,7 +36,8 @@
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Qty"
+ "label": "Qty",
+ "reqd": 1
},
{
"fieldname": "picked_qty",
@@ -180,7 +181,7 @@
],
"istable": 1,
"links": [],
- "modified": "2020-06-24 17:18:57.357120",
+ "modified": "2021-09-28 12:02:16.923056",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List Item",
diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item.py
index 8797b8d..4cd81f7 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.py
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class PickListItem(Document):
pass
diff --git a/erpnext/stock/doctype/price_list/price_list.css b/erpnext/stock/doctype/price_list/price_list.css
index 61b0694..6832954 100644
--- a/erpnext/stock/doctype/price_list/price_list.css
+++ b/erpnext/stock/doctype/price_list/price_list.css
@@ -4,4 +4,4 @@
.table-grid thead tr {
height: 50px;
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/price_list/price_list.js b/erpnext/stock/doctype/price_list/price_list.js
index c362b5a..9291498 100644
--- a/erpnext/stock/doctype/price_list/price_list.js
+++ b/erpnext/stock/doctype/price_list/price_list.js
@@ -11,4 +11,4 @@
frappe.set_route("Report", "Item Price");
}, "fa fa-money");
}
-});
\ 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 33713fa..01040c6 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -2,17 +2,21 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _, throw
-from frappe.utils import cint
from frappe.model.document import Document
-import frappe.defaults
+from frappe.utils import cint
+
class PriceList(Document):
def validate(self):
if not cint(self.buying) and not cint(self.selling):
throw(_("Price List must be applicable for Buying or Selling"))
+ if not self.is_new():
+ self.check_impact_on_shopping_cart()
+
def on_update(self):
self.set_default_if_missing()
self.update_item_price()
@@ -32,6 +36,19 @@
buying=%s, selling=%s, modified=NOW() where price_list=%s""",
(self.currency, cint(self.buying), cint(self.selling), self.name))
+ def check_impact_on_shopping_cart(self):
+ "Check if Price List currency change impacts Shopping Cart."
+ from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ validate_cart_settings,
+ )
+
+ doc_before_save = self.get_doc_before_save()
+ currency_changed = self.currency != doc_before_save.currency
+ affects_cart = self.name == frappe.get_cached_value("Shopping Cart Settings", None, "price_list")
+
+ if currency_changed and affects_cart:
+ validate_cart_settings()
+
def on_trash(self):
self.delete_price_list_details_key()
@@ -62,4 +79,4 @@
frappe.cache().hset("price_list_details", price_list, price_list_details)
- return price_list_details or {}
\ No newline at end of file
+ return price_list_details or {}
diff --git a/erpnext/stock/doctype/price_list/test_price_list.js b/erpnext/stock/doctype/price_list/test_price_list.js
deleted file mode 100644
index fe4e07b..0000000
--- a/erpnext/stock/doctype/price_list/test_price_list.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Price List", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Price List
- () => frappe.tests.make('Price List', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/price_list/test_price_list.py b/erpnext/stock/doctype/price_list/test_price_list.py
index 5979c86..baf6170 100644
--- a/erpnext/stock/doctype/price_list/test_price_list.py
+++ b/erpnext/stock/doctype/price_list/test_price_list.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
# test_ignore = ["Item"]
-test_records = frappe.get_test_records('Price List')
\ No newline at end of file
+test_records = frappe.get_test_records('Price List')
diff --git a/erpnext/stock/doctype/price_list/test_price_list_uom.js b/erpnext/stock/doctype/price_list/test_price_list_uom.js
index 7fbce7d..3896c0e 100644
--- a/erpnext/stock/doctype/price_list/test_price_list_uom.js
+++ b/erpnext/stock/doctype/price_list/test_price_list_uom.js
@@ -55,4 +55,4 @@
() => done()
]);
-});
\ No newline at end of file
+});
diff --git a/erpnext/stock/doctype/price_list_country/price_list_country.py b/erpnext/stock/doctype/price_list_country/price_list_country.py
index db1a060..a57729f 100644
--- a/erpnext/stock/doctype/price_list_country/price_list_country.py
+++ b/erpnext/stock/doctype/price_list_country/price_list_country.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class PriceListCountry(Document):
pass
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 44fb736..112dded 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -1098,7 +1098,8 @@
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
- "label": "Scan Barcode"
+ "label": "Scan Barcode",
+ "options": "Barcode"
},
{
"fieldname": "billing_address",
@@ -1139,6 +1140,7 @@
"fetch_from": "supplier.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Represents Company",
"options": "Company",
"read_only": 1
@@ -1148,7 +1150,7 @@
"idx": 261,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-25 00:15:12.239017",
+ "modified": "2021-09-28 13:11:10.181328",
"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 ece6d6f..47c8df9 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -2,21 +2,19 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-
-from frappe.utils import flt, cint, nowdate
-
-from frappe import throw, _
-import frappe.defaults
-from frappe.utils import getdate
-from erpnext.controllers.buying_controller import BuyingController
-from erpnext.accounts.utils import get_account_currency
+from frappe import _, throw
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
-from erpnext.buying.utils import check_on_hold_or_closed_status
+from frappe.utils import cint, flt, getdate, nowdate
+from six import iteritems
+
+from erpnext.accounts.utils import get_account_currency
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 six import iteritems
+from erpnext.buying.utils import check_on_hold_or_closed_status
+from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction
form_grid_templates = {
@@ -254,7 +252,9 @@
return process_gl_map(gl_entries)
def make_item_gl_entries(self, gl_entries, warehouse_account=None):
- from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_purchase_document_details
+ from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import (
+ get_purchase_document_details,
+ )
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
@@ -842,7 +842,8 @@
"doctype": "Stock Entry Detail",
"field_map": {
"warehouse": "s_warehouse",
- "parent": "reference_purchase_receipt"
+ "parent": "reference_purchase_receipt",
+ "batch_no": "batch_no"
},
},
}, target_doc, set_missing_values)
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
index 3832c82..b60850f 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from frappe import _
+
def get_data():
return {
'fieldname': 'purchase_receipt_no',
diff --git a/erpnext/stock/doctype/purchase_receipt/regional/india.js b/erpnext/stock/doctype/purchase_receipt/regional/india.js
index b4f1201..2d982cc 100644
--- a/erpnext/stock/doctype/purchase_receipt/regional/india.js
+++ b/erpnext/stock/doctype/purchase_receipt/regional/india.js
@@ -1,3 +1,3 @@
{% include "erpnext/regional/india/taxes.js" %}
-erpnext.setup_auto_gst_taxation('Purchase Receipt');
\ No newline at end of file
+erpnext.setup_auto_gst_taxation('Purchase Receipt');
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 05df161..044856c 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2,20 +2,22 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import unittest
+
import json
-import frappe, erpnext
-import frappe.defaults
-from frappe.utils import cint, flt, cstr, today, random_string, add_days
-from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
-from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError
-from erpnext.accounts.doctype.account.test_account import get_inventory_account
-from erpnext.stock.doctype.item.test_item import make_item
+import unittest
+
+import frappe
+from frappe.utils import add_days, cint, cstr, flt, today
from six import iteritems
-from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
+
+import erpnext
+from erpnext.accounts.doctype.account.test_account import get_inventory_account
+from erpnext.stock.doctype.item.test_item import create_item, make_item
+from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
+
class TestPurchaseReceipt(unittest.TestCase):
def setUp(self):
@@ -275,11 +277,16 @@
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 (
+ make_rm_stock_entry as make_subcontract_transfer_entry,
+ )
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import (
+ create_purchase_order,
+ make_subcontracted_item,
+ update_backflush_based_on,
+ )
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- from erpnext.buying.doctype.purchase_order.test_purchase_order import (update_backflush_based_on,
- make_subcontracted_item, create_purchase_order)
- from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt,
- make_rm_stock_entry as make_subcontract_transfer_entry)
update_backflush_based_on("Material Transferred for Subcontract")
item_code = "_Test Subcontracted FG Item 1"
@@ -326,7 +333,6 @@
self.assertRaises(frappe.ValidationError, pr2.submit)
frappe.db.rollback()
-
def test_serial_no_supplier(self):
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
self.assertEqual(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"),
@@ -527,7 +533,9 @@
pr.cancel()
def test_closed_purchase_receipt(self):
- from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status
+ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
+ update_purchase_receipt_status,
+ )
pr = make_purchase_receipt(do_not_submit=True)
pr.submit()
@@ -540,9 +548,11 @@
def test_pr_billing_status(self):
# PO -> PR1 -> PI and PO -> PI and PO -> PR2
+ from erpnext.buying.doctype.purchase_order.purchase_order import (
+ make_purchase_invoice as make_purchase_invoice_from_po,
+ )
+ from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
- from erpnext.buying.doctype.purchase_order.purchase_order \
- import make_purchase_receipt, make_purchase_invoice as make_purchase_invoice_from_po
po = create_purchase_order()
@@ -749,7 +759,10 @@
pr.cancel()
def test_make_purchase_invoice_from_pr_for_returned_qty(self):
- from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import (
+ create_pr_against_po,
+ create_purchase_order,
+ )
po = create_purchase_order()
pr = create_pr_against_po(po.name)
@@ -880,10 +893,15 @@
def test_subcontracted_pr_for_multi_transfer_batches(self):
+ from erpnext.buying.doctype.purchase_order.purchase_order import (
+ make_purchase_receipt,
+ make_rm_stock_entry,
+ )
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import (
+ create_purchase_order,
+ update_backflush_based_on,
+ )
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry, make_purchase_receipt
- from erpnext.buying.doctype.purchase_order.test_purchase_order import (update_backflush_based_on,
- create_purchase_order)
update_backflush_based_on("Material Transferred for Subcontract")
item_code = "_Test Subcontracted FG Item 3"
@@ -953,8 +971,7 @@
- Create PI from PO and submit
- Create PR from PO and submit
"""
- from erpnext.buying.doctype.purchase_order import test_purchase_order
- from erpnext.buying.doctype.purchase_order import purchase_order
+ from erpnext.buying.doctype.purchase_order import purchase_order, test_purchase_order
po = test_purchase_order.create_purchase_order()
@@ -975,8 +992,7 @@
- Create partial PI from PO and submit
- Create PR from PO and submit
"""
- from erpnext.buying.doctype.purchase_order import test_purchase_order
- from erpnext.buying.doctype.purchase_order import purchase_order
+ from erpnext.buying.doctype.purchase_order import purchase_order, test_purchase_order
po = test_purchase_order.create_purchase_order()
@@ -1039,8 +1055,12 @@
frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', before_test_value)
def test_purchase_receipt_with_exchange_rate_difference(self):
- from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice as create_purchase_invoice
- from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_purchase_receipt as create_purchase_receipt
+ from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import (
+ make_purchase_receipt as create_purchase_receipt,
+ )
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
+ make_purchase_invoice as create_purchase_invoice,
+ )
pi = create_purchase_invoice(company="_Test Company with perpetual inventory",
cost_center = "Main - TCP1",
@@ -1066,10 +1086,18 @@
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
- from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
+ create_payment_terms_template,
+ )
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
- from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, make_pr_against_po
- from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import (
+ create_purchase_order,
+ make_pr_against_po,
+ )
+ from erpnext.selling.doctype.sales_order.test_sales_order import (
+ automatically_fetch_payment_terms,
+ compare_payment_schedules,
+ )
automatically_fetch_payment_terms()
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 82cc98e..3efa66e 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -10,6 +10,7 @@
"barcode",
"section_break_2",
"item_code",
+ "product_bundle",
"supplier_part_no",
"column_break_2",
"item_name",
@@ -956,12 +957,19 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "product_bundle",
+ "fieldtype": "Link",
+ "label": "Product Bundle",
+ "options": "Product Bundle",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2021-03-29 04:17:00.336298",
+ "modified": "2021-09-01 16:02:40.338597",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
index b79bb5d..2d25140 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class PurchaseReceiptItem(Document):
pass
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
index 0f50bcd..aa9d896 100644
--- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -3,16 +3,20 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import copy
import json
from collections import defaultdict
-from six import string_types
+
+import frappe
from frappe import _
-from frappe.utils import flt, floor, nowdate, cint
from frappe.model.document import Document
-from erpnext.stock.utils import get_stock_balance
+from frappe.utils import cint, floor, flt, nowdate
+from six import string_types
+
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.utils import get_stock_balance
+
class PutawayRule(Document):
def validate(self):
@@ -232,4 +236,4 @@
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 ""
\ No newline at end of file
+ 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 86f7dc3..0aa7610 100644
--- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -2,14 +2,18 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.stock.get_item_details import get_conversion_factor
-from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+import frappe
+
from erpnext.stock.doctype.batch.test_batch import make_new_batch
+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.test_stock_entry import make_stock_entry
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.stock.get_item_details import get_conversion_factor
+
class TestPutawayRule(unittest.TestCase):
def setUp(self):
@@ -386,4 +390,4 @@
if not args.do_not_save:
putaway.save()
- return putaway
\ No newline at end of file
+ return putaway
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js
index f7565fd..d08dc3e 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js
@@ -81,4 +81,4 @@
});
}
},
-});
\ No newline at end of file
+});
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index 469511a..8b2f8da 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -2,13 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
+from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
-from frappe import _
-from frappe.utils import flt, cint
-from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
- import get_template_details
+from frappe.utils import cint, flt
+
+from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template import (
+ get_template_details,
+)
+
class QualityInspection(Document):
def validate(self):
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.js b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.js
deleted file mode 100644
index 327484e..0000000
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Inspection", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quality Inspection
- () => frappe.tests.make('Quality Inspection', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
index 8678422..fa68201 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityInspectionParameter(Document):
pass
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py b/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
index cefdc08..f3041aa 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
+++ b/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestQualityInspectionParameter(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
index 1a3b1a0..b5e28f3 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class QualityInspectionParameterGroup(Document):
pass
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
index 212d4b8..ded47e8 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestQualityInspectionParameterGroup(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py
index 65188a2..7b56603 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class QualityInspectionReading(Document):
- pass
\ No newline at end of file
+ pass
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 01d2031..50e28a6 100644
--- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe.model.document import Document
+
class QualityInspectionTemplate(Document):
pass
@@ -16,4 +18,4 @@
fields=["specification", "value", "acceptance_formula",
"numeric", "formula_based_criteria", "min_value", "max_value"],
filters={'parenttype': 'Quality Inspection Template', 'parent': template},
- order_by="idx")
\ No newline at end of file
+ order_by="idx")
diff --git a/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.js b/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.js
deleted file mode 100644
index 879c262..0000000
--- a/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Inspection Template", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quality Inspection Template
- () => frappe.tests.make('Quality Inspection Template', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.py
index b16efa8..6286523 100644
--- a/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/test_quality_inspection_template.py
@@ -5,5 +5,6 @@
import unittest
+
class TestQualityInspectionTemplate(unittest.TestCase):
pass
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 efa9519..8ca5521 100644
--- a/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
+++ b/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.model.document import Document
+
from erpnext.stock.utils import get_stock_balance, get_stock_value_on
+
class QuickStockBalance(Document):
pass
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 b22759d..d86e52f 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -3,14 +3,22 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from rq.timeouts import JobTimeoutException
-from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form, add_to_date, now, today, time_diff_in_hours
-from erpnext.stock.stock_ledger import repost_future_sle
-from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
-from frappe.utils.user import get_users_with_role
+
+import frappe
from frappe import _
+from frappe.model.document import Document
+from frappe.utils import cint, get_link_to_form, get_weekday, now, nowtime, today
+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.stock.stock_ledger import repost_future_sle
+
+
class RepostItemValuation(Document):
def validate(self):
self.set_status()
@@ -118,6 +126,9 @@
frappe.sendmail(recipients=recipients, subject=subject, message=message)
def repost_entries():
+ if not in_configured_timeslot():
+ return
+
riv_entries = get_repost_item_valuation_entries()
for row in riv_entries:
@@ -136,3 +147,26 @@
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)
+
+
+def in_configured_timeslot(repost_settings=None, current_time=None):
+ """Check if current time is in configured timeslot for reposting."""
+
+ if repost_settings is None:
+ repost_settings = frappe.get_cached_doc("Stock Reposting Settings")
+
+ if not repost_settings.limit_reposting_timeslot:
+ return True
+
+ if get_weekday() == repost_settings.limits_dont_apply_on:
+ return True
+
+ start_time = repost_settings.start_time
+ end_time = repost_settings.end_time
+
+ now_time = current_time or nowtime()
+
+ if start_time < end_time:
+ return end_time >= now_time >= start_time
+ else:
+ return now_time >= start_time or now_time <= end_time
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 13ceb68..c086f93 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,10 +1,72 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-from __future__ import unicode_literals
-# import frappe
import unittest
+import frappe
+
+from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
+ in_configured_timeslot,
+)
+
+
class TestRepostItemValuation(unittest.TestCase):
- pass
+ def test_repost_time_slot(self):
+ repost_settings = frappe.get_doc("Stock Reposting Settings")
+
+ positive_cases = [
+ {"limit_reposting_timeslot": 0},
+ {
+ "limit_reposting_timeslot": 1,
+ "start_time": "18:00:00",
+ "end_time": "09:00:00",
+ "current_time": "20:00:00",
+ },
+ {
+ "limit_reposting_timeslot": 1,
+ "start_time": "09:00:00",
+ "end_time": "18:00:00",
+ "current_time": "12:00:00",
+ },
+ {
+ "limit_reposting_timeslot": 1,
+ "start_time": "23:00:00",
+ "end_time": "09:00:00",
+ "current_time": "2:00:00",
+ },
+ ]
+
+ for case in positive_cases:
+ repost_settings.update(case)
+ self.assertTrue(
+ in_configured_timeslot(repost_settings, case.get("current_time")),
+ msg=f"Exepcted true from : {case}",
+ )
+
+ negative_cases = [
+ {
+ "limit_reposting_timeslot": 1,
+ "start_time": "18:00:00",
+ "end_time": "09:00:00",
+ "current_time": "09:01:00",
+ },
+ {
+ "limit_reposting_timeslot": 1,
+ "start_time": "09:00:00",
+ "end_time": "18:00:00",
+ "current_time": "19:00:00",
+ },
+ {
+ "limit_reposting_timeslot": 1,
+ "start_time": "23:00:00",
+ "end_time": "09:00:00",
+ "current_time": "22:00:00",
+ },
+ ]
+
+ for case in negative_cases:
+ repost_settings.update(case)
+ self.assertFalse(
+ in_configured_timeslot(repost_settings, case.get("current_time")),
+ msg=f"Exepcted false from : {case}",
+ )
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 70312bc..a9254fb 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -2,19 +2,20 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import json
+import frappe
+from frappe import ValidationError, _
from frappe.model.naming import make_autoname
-from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate, get_link_to_form
-from erpnext.stock.get_item_details import get_reserved_qty_for_so
-
-from frappe import _, ValidationError
-
-from erpnext.controllers.stock_controller import StockController
+from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate
from six import string_types
from six.moves import map
+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
@@ -573,7 +574,7 @@
if batch_nos:
try:
filters["batch_no"] = json.loads(batch_nos) if (type(json.loads(batch_nos)) == list) else [json.loads(batch_nos)]
- except:
+ except Exception:
filters["batch_no"] = [batch_nos]
if posting_date:
@@ -610,7 +611,9 @@
return reserved_sr_nos
-def fetch_serial_numbers(filters, qty, do_not_include=[]):
+def fetch_serial_numbers(filters, qty, do_not_include=None):
+ if do_not_include is None:
+ do_not_include = []
batch_join_selection = ""
batch_no_condition = ""
batch_nos = filters.get("batch_no")
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.js b/erpnext/stock/doctype/serial_no/test_serial_no.js
deleted file mode 100644
index bf82932..0000000
--- a/erpnext/stock/doctype/serial_no/test_serial_no.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Serial No", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Serial No
- () => frappe.tests.make('Serial No', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index b9a58cf..818c163 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -5,12 +5,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, unittest
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+import unittest
+
+import frappe
+
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.serial_no.serial_no import get_serial_nos
+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"]
@@ -18,6 +21,7 @@
from erpnext.stock.doctype.serial_no.serial_no import *
+
class TestSerialNo(unittest.TestCase):
def test_cannot_create_direct(self):
frappe.delete_doc_if_exists("Serial No", "_TCSER0001")
@@ -193,4 +197,4 @@
frappe.db.rollback()
def tearDown(self):
- frappe.db.rollback()
\ No newline at end of file
+ frappe.db.rollback()
diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js
index ce2906e..13a17a2 100644
--- a/erpnext/stock/doctype/shipment/shipment.js
+++ b/erpnext/stock/doctype/shipment/shipment.js
@@ -150,8 +150,8 @@
frm.set_value('pickup_contact_name', '');
frm.set_value('pickup_contact', '');
}
- frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.")
- + "</br>" + __("Please set Email/Phone for the contact")
+ frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.")
+ + "</br>" + __("Please set Email/Phone for the contact")
+ ` <a href='/app/contact/${contact_name}'>${contact_name}</a>`);
}
let contact_display = r.message.contact_display;
@@ -244,8 +244,8 @@
frm.set_value('pickup_company', '');
frm.set_value('pickup_contact', '');
}
- frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "</br>"
- + __("Please first set Last Name, Email and Phone for the user")
+ frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "</br>"
+ + __("Please first set Last Name, Email and Phone for the user")
+ ` <a href="/app/user/${frappe.session.user}">${frappe.session.user}</a>`);
}
let contact_display = r.full_name;
diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py
index 01fcee4..2cacd0d 100644
--- a/erpnext/stock/doctype/shipment/shipment.py
+++ b/erpnext/stock/doctype/shipment/shipment.py
@@ -3,12 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, get_time
-from frappe.model.document import Document
-from erpnext.accounts.party import get_party_shipping_address
from frappe.contacts.doctype.contact.contact import get_default_contact
+from frappe.model.document import Document
+from frappe.utils import flt, get_time
+
+from erpnext.accounts.party import get_party_shipping_address
+
class Shipment(Document):
def validate(self):
diff --git a/erpnext/stock/doctype/shipment/shipment_list.js b/erpnext/stock/doctype/shipment/shipment_list.js
index 52b052c..ae6a3c1 100644
--- a/erpnext/stock/doctype/shipment/shipment_list.js
+++ b/erpnext/stock/doctype/shipment/shipment_list.js
@@ -5,4 +5,4 @@
return [__("Booked"), "green"];
}
}
-};
\ No newline at end of file
+};
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index 9c3e22f..9914cf8 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -2,12 +2,15 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
+
+import unittest
from datetime import date, timedelta
import frappe
-import unittest
+
from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
+
class TestShipment(unittest.TestCase):
def test_shipment_from_delivery_note(self):
delivery_note = create_test_delivery_note()
@@ -24,7 +27,7 @@
customer = get_shipment_customer()
item = get_shipment_item(company.name)
posting_date = date.today() + timedelta(days=1)
-
+
create_material_receipt(item, company.name)
delivery_note = frappe.new_doc("Delivery Note")
delivery_note.company = company.name
@@ -73,7 +76,7 @@
shipment.pickup_to = '17:00'
shipment.description_of_content = 'unit test entry'
for delivery_note in delivery_notes:
- shipment.append('shipment_delivery_note',
+ shipment.append('shipment_delivery_note',
{
"delivery_note": delivery_note.name
}
@@ -222,7 +225,7 @@
)
stock.insert()
stock.submit()
-
+
def create_shipment_item(item_name, company_name):
item = frappe.new_doc("Item")
diff --git a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
index 4342151..795c952 100644
--- a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
+++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ShipmentDeliveryNote(Document):
pass
diff --git a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
index 53e6ed5..69fecb6 100644
--- a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
+++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ShipmentParcel(Document):
pass
diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
index 2a8d58d..0eaa2d3 100644
--- a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
+++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ShipmentParcelTemplate(Document):
pass
diff --git a/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py b/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py
index 6e2caa7..5f2a399 100644
--- a/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py
+++ b/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestShipmentParcelTemplate(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 908020d..ac8303e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -278,7 +278,7 @@
get_query_filters: {
docstatus: 1,
material_request_type: ["in", allowed_request_types],
- status: ["not in", ["Transferred", "Issued"]]
+ status: ["not in", ["Transferred", "Issued", "Cancelled", "Stopped"]]
}
})
}, __("Get Items From"));
@@ -323,6 +323,12 @@
attach_bom_items(frm.doc.bom_no)
},
+ before_save: function(frm) {
+ frm.doc.items.forEach((item) => {
+ item.uom = item.uom || item.stock_uom;
+ })
+ },
+
stock_entry_type: function(frm){
frm.remove_custom_button('Bill of Materials', "Get Items From");
frm.events.show_bom_custom_button(frm);
@@ -548,44 +554,7 @@
calculate_basic_amount: function(frm, item) {
item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
precision("basic_amount", item));
-
- frm.events.calculate_amount(frm);
- },
-
- calculate_amount: function(frm) {
frm.events.calculate_total_additional_costs(frm);
- let total_basic_amount = 0;
- if (in_list(["Repack", "Manufacture"], frm.doc.purpose)) {
- total_basic_amount = frappe.utils.sum(
- (frm.doc.items || []).map(function(i) {
- return i.is_finished_item ? flt(i.basic_amount) : 0;
- })
- );
- } else {
- total_basic_amount = frappe.utils.sum(
- (frm.doc.items || []).map(function(i) {
- return i.t_warehouse ? flt(i.basic_amount) : 0;
- })
- );
- }
- for (let i in frm.doc.items) {
- let item = frm.doc.items[i];
-
- if (((in_list(["Repack", "Manufacture"], frm.doc.purpose) && item.is_finished_item) || item.t_warehouse) && total_basic_amount) {
- item.additional_cost = (flt(item.basic_amount) / total_basic_amount) * frm.doc.total_additional_costs;
- } else {
- item.additional_cost = 0;
- }
-
- item.amount = flt(item.basic_amount + flt(item.additional_cost), precision("amount", item));
-
- if (flt(item.transfer_qty)) {
- item.valuation_rate = flt(flt(item.basic_rate) + (flt(item.additional_cost) / flt(item.transfer_qty)),
- precision("valuation_rate", item));
- }
- }
-
- refresh_field('items');
},
calculate_total_additional_costs: function(frm) {
@@ -781,11 +750,6 @@
amount: function(frm, cdt, cdn) {
frm.events.set_base_amount(frm, cdt, cdn);
- // Adding this check because same table in used in LCV
- // This causes an error if you try to post an LCV immediately after a Stock Entry
- if (frm.doc.doctype == 'Stock Entry') {
- frm.events.calculate_amount(frm);
- }
},
expense_account: function(frm, cdt, cdn) {
@@ -1100,4 +1064,4 @@
);
}
-$.extend(cur_frm.cscript, new erpnext.stock.StockEntry({frm: cur_frm}));
+extend_cscript(cur_frm.cscript, new erpnext.stock.StockEntry({frm: cur_frm}));
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 523d332..2f37778 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -84,8 +84,6 @@
"oldfieldtype": "Section Break"
},
{
- "allow_on_submit": 1,
- "default": "{purpose}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@@ -355,6 +353,7 @@
},
{
"fieldname": "scan_barcode",
+ "options": "Barcode",
"fieldtype": "Data",
"label": "Scan Barcode"
},
@@ -629,7 +628,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-26 17:07:58.015737",
+ "modified": "2021-08-20 19:19:31.514846",
"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 7b31d2f..bd7d22b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -2,27 +2,40 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-import frappe.defaults
+
+import json
+from collections import defaultdict
+
+import frappe
from frappe import _
-from frappe.utils import cstr, cint, flt, comma_or, getdate, nowdate, formatdate, format_time
-from erpnext.stock.utils import get_incoming_rate
-from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError, get_valuation_rate
-from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor, get_reserved_qty_for_so
-from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
-from erpnext.setup.doctype.brand.brand import get_brand_defaults
-from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos, get_batch_qty
-from erpnext.stock.doctype.item.item import get_item_defaults
-from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, add_additional_cost
-from erpnext.stock.utils import get_bin
from frappe.model.mapper import get_mapped_doc
-from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
-from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
+from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate
+from six import iteritems, itervalues, string_types
+
+import erpnext
from erpnext.accounts.general_ledger import process_gl_map
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
-import json
+from erpnext.manufacturing.doctype.bom.bom import add_additional_cost, validate_bom_no
+from erpnext.setup.doctype.brand.brand import get_brand_defaults
+from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
+from erpnext.stock.doctype.batch.batch import get_batch_no, get_batch_qty, set_batch_nos
+from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.stock.doctype.serial_no.serial_no import (
+ get_serial_nos,
+ update_serial_nos_after_submit,
+)
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
+ OpeningEntryAccountError,
+)
+from erpnext.stock.get_item_details import (
+ get_bin_details,
+ get_conversion_factor,
+ get_default_cost_center,
+ get_reserved_qty_for_so,
+)
+from erpnext.stock.stock_ledger import NegativeStockError, get_previous_sle, get_valuation_rate
+from erpnext.stock.utils import get_bin, get_incoming_rate
-from six import string_types, itervalues, iteritems
class IncorrectValuationRateError(frappe.ValidationError): pass
class DuplicateEntryForWorkOrderError(frappe.ValidationError): pass
@@ -58,6 +71,7 @@
self.validate_posting_time()
self.validate_purpose()
+ self.set_title()
self.validate_item()
self.validate_customer_provided_item()
self.validate_qty()
@@ -271,7 +285,7 @@
item_wise_qty = {}
if self.purpose == "Manufacture" and self.work_order:
for d in self.items:
- if d.is_finished_item:
+ if d.is_finished_item or d.is_process_loss:
item_wise_qty.setdefault(d.item_code, []).append(d.qty)
for item_code, qty_list in iteritems(item_wise_qty):
@@ -317,9 +331,6 @@
d.s_warehouse = self.from_warehouse
d.t_warehouse = self.to_warehouse
- if not (d.s_warehouse or d.t_warehouse):
- frappe.throw(_("Atleast one warehouse is mandatory"))
-
if self.purpose in source_mandatory and not d.s_warehouse:
if self.from_warehouse:
d.s_warehouse = self.from_warehouse
@@ -332,9 +343,10 @@
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:
+ if d.is_finished_item or d.is_scrap_item or d.is_process_loss:
d.s_warehouse = None
if not d.t_warehouse:
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
@@ -346,6 +358,9 @@
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"):
# check if work order is entered
@@ -463,7 +478,7 @@
"""
# 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)
+ 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'):
@@ -484,6 +499,8 @@
raise_error_if_no_rate=raise_error_if_no_rate)
d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
+ if d.is_process_loss:
+ d.basic_rate = flt(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):
@@ -538,22 +555,27 @@
def distribute_additional_costs(self):
# If no incoming items, set additional costs blank
- if not any([d.item_code for d in self.items if d.t_warehouse]):
+ if not any(d.item_code for d in self.items if d.t_warehouse):
self.additional_costs = []
- self.total_additional_costs = sum([flt(t.base_amount) for t in self.get("additional_costs")])
+ self.total_additional_costs = sum(flt(t.base_amount) for t in self.get("additional_costs"))
if self.purpose in ("Repack", "Manufacture"):
- incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item])
+ incoming_items_cost = sum(flt(t.basic_amount) for t in self.get("items") if t.is_finished_item)
else:
- incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
+ incoming_items_cost = sum(flt(t.basic_amount) for t in self.get("items") if t.t_warehouse)
- if incoming_items_cost:
- for d in self.get("items"):
- if (self.purpose in ("Repack", "Manufacture") and d.is_finished_item) or d.t_warehouse:
- d.additional_cost = (flt(d.basic_amount) / incoming_items_cost) * self.total_additional_costs
- else:
- d.additional_cost = 0
+ if not incoming_items_cost:
+ return
+
+ for d in self.get("items"):
+ if self.purpose in ("Repack", "Manufacture") and not d.is_finished_item:
+ d.additional_cost = 0
+ continue
+ elif not d.t_warehouse:
+ d.additional_cost = 0
+ continue
+ d.additional_cost = (flt(d.basic_amount) / incoming_items_cost) * self.total_additional_costs
def update_valuation_rate(self):
for d in self.get("items"):
@@ -668,7 +690,7 @@
def validate_bom(self):
for d in self.get('items'):
- if d.bom_no and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
+ 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)
@@ -788,7 +810,11 @@
def get_gl_entries(self, warehouse_account):
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
- total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
+ if self.purpose in ("Repack", "Manufacture"):
+ total_basic_amount = sum(flt(t.basic_amount) for t in self.get("items") if t.is_finished_item)
+ else:
+ total_basic_amount = sum(flt(t.basic_amount) for t in self.get("items") if t.t_warehouse)
+
divide_based_on = total_basic_amount
if self.get("additional_costs") and not total_basic_amount:
@@ -799,20 +825,24 @@
for t in self.get("additional_costs"):
for d in self.get("items"):
- if d.t_warehouse:
- 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
- })
+ if self.purpose in ("Repack", "Manufacture") and not d.is_finished_item:
+ continue
+ elif not d.t_warehouse:
+ continue
- multiply_based_on = d.basic_amount if total_basic_amount else d.qty
+ 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)][t.expense_account]["amount"] += \
- flt(t.amount * multiply_based_on) / divide_based_on
+ 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]["base_amount"] += \
- flt(t.base_amount * multiply_based_on) / divide_based_on
+ 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"] += \
+ flt(t.base_amount * multiply_based_on) / divide_based_on
if item_account_wise_additional_cost:
for d in self.get("items"):
@@ -1041,6 +1071,7 @@
self.set_scrap_items()
self.set_actual_qty()
+ self.update_items_for_process_loss()
self.validate_customer_provided_item()
self.calculate_rate_and_amount()
@@ -1174,13 +1205,88 @@
# 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)
+ fetch_exploded = 0, fetch_scrap_items = 1) or {}
for item in itervalues(item_dict):
item.from_warehouse = ""
item.is_scrap_item = 1
+
+ for row in self.get_scrap_items_from_job_card():
+ if row.stock_qty <= 0:
+ continue
+
+ item_row = item_dict.get(row.item_code)
+ 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_dict[row.item_code] = item_row
+
return item_dict
+ def get_scrap_items_from_job_card(self):
+ if not self.pro_doc:
+ self.set_work_order_details()
+
+ scrap_items = frappe.db.sql('''
+ SELECT
+ JCSI.item_code, JCSI.item_name, SUM(JCSI.stock_qty) as stock_qty, JCSI.stock_uom, JCSI.description
+ FROM
+ `tabJob Card` JC, `tabJob Card Scrap Item` JCSI
+ WHERE
+ JCSI.parent = JC.name AND JC.docstatus = 1
+ AND JCSI.item_code IS NOT NULL AND JC.work_order = %s
+ GROUP BY
+ JCSI.item_code
+ ''', self.work_order, as_dict=1)
+
+ pending_qty = flt(self.pro_doc.qty) - flt(self.pro_doc.produced_qty)
+ if pending_qty <=0:
+ return []
+
+ used_scrap_items = self.get_used_scrap_items()
+ for row in scrap_items:
+ row.stock_qty -= flt(used_scrap_items.get(row.item_code))
+ row.stock_qty = (row.stock_qty) * flt(self.fg_completed_qty) / flt(pending_qty)
+
+ 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')):
+ row.stock_qty = frappe.utils.ceil(row.stock_qty)
+
+ return scrap_items
+
+ 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`'
+ ],
+ 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:
+ used_scrap_items[row.item_code] += row.qty
+
+ return used_scrap_items
+
def get_unconsumed_raw_materials(self):
wo = frappe.get_doc("Work Order", self.work_order)
wo_items = frappe.get_all('Work Order Item',
@@ -1195,10 +1301,10 @@
wo_item_qty = item.transferred_qty or item.required_qty
- req_qty_each = (
- (flt(wo_item_qty) - flt(item.consumed_qty)) /
- (flt(work_order_qty) - flt(wo.produced_qty))
- )
+ wo_qty_consumed = flt(wo_item_qty) - flt(item.consumed_qty)
+ wo_qty_to_produce = flt(work_order_qty) - flt(wo.produced_qty)
+
+ req_qty_each = (wo_qty_consumed) / (wo_qty_to_produce or 1)
qty = req_qty_each * flt(self.fg_completed_qty)
@@ -1247,9 +1353,9 @@
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)
+ manufacturing_qty = flt(po_qty.qty) or 1
produced_qty = flt(po_qty.produced_qty)
- trans_qty = flt(po_qty.material_transferred_for_manufacturing)
+ trans_qty = flt(po_qty.material_transferred_for_manufacturing) or 1
for item in transferred_materials:
qty= item.qty
@@ -1398,9 +1504,10 @@
get_default_cost_center(item_dict[d], company = self.company))
se_child.is_finished_item = item_dict[d].get("is_finished_item", 0)
se_child.is_scrap_item = item_dict[d].get("is_scrap_item", 0)
+ se_child.is_process_loss = item_dict[d].get("is_process_loss", 0)
- for field in ["idx", "po_detail", "original_item",
- "expense_account", "description", "item_name", "serial_no", "batch_no"]:
+ for field in ["idx", "po_detail", "original_item", "expense_account",
+ "description", "item_name", "serial_no", "batch_no", "allow_zero_valuation_rate"]:
if item_dict[d].get(field):
se_child.set(field, item_dict[d].get(field))
@@ -1485,7 +1592,8 @@
qty_to_reserve -= reserved_qty[0][0]
if qty_to_reserve > 0:
for item in self.items:
- if item.item_code == item_code:
+ has_serial_no = frappe.get_cached_value("Item", item.item_code, "has_serial_no")
+ if item.item_code == item_code and has_serial_no:
serial_nos = (item.serial_no).split("\n")
for serial_no in serial_nos:
if qty_to_reserve > 0:
@@ -1577,6 +1685,29 @@
material_requests.append(material_request)
frappe.db.set_value('Material Request', material_request, 'transfer_status', status)
+ def update_items_for_process_loss(self):
+ process_loss_dict = {}
+ for d in self.get("items"):
+ if not d.is_process_loss:
+ continue
+
+ 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
+
+ if d.item_code not in process_loss_dict:
+ process_loss_dict[d.item_code] = [flt(0), flt(0)]
+ process_loss_dict[d.item_code][0] += flt(d.transfer_qty)
+ process_loss_dict[d.item_code][1] += flt(d.qty)
+
+ for d in self.get("items"):
+ if not d.is_finished_item or d.item_code not in process_loss_dict:
+ continue
+ # Assumption: 1 finished item has 1 row.
+ d.transfer_qty -= process_loss_dict[d.item_code][0]
+ d.qty -= process_loss_dict[d.item_code][1]
+
def set_serial_no_batch_for_finished_good(self):
args = {}
if self.pro_doc.serial_no:
@@ -1607,6 +1738,14 @@
return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos)))
+ def set_title(self):
+ if frappe.flags.in_import and self.title:
+ # Allow updating title during data import/update
+ return
+
+ self.title = self.purpose
+
+
@frappe.whitelist()
def move_sample_to_retention_warehouse(company, items):
if isinstance(items, string_types):
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index 563fcb0..f54dc46 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -2,11 +2,14 @@
# See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import cint, flt
+import frappe
+from frappe.utils import cint, flt
from six import string_types
+import erpnext
+
+
@frappe.whitelist()
def make_stock_entry(**args):
'''Helper function to make a Stock Entry
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index a0e7051..c9d0af5 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -2,21 +2,33 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, unittest
-import frappe.defaults
-from frappe.utils import flt, nowdate, nowtime
-from erpnext.stock.doctype.serial_no.serial_no import *
-from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
-from erpnext.stock.stock_ledger import get_previous_sle
+
+import unittest
+
+import frappe
from frappe.permissions import add_user_permission, remove_user_permission
-from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
-from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make_item_variant, create_item
-from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.accounts.doctype.account.test_account import get_inventory_account
-from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse, make_stock_in_entry
-from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
+from frappe.utils import flt, nowdate, nowtime
from six import iteritems
+from erpnext.accounts.doctype.account.test_account import get_inventory_account
+from erpnext.stock.doctype.item.test_item import (
+ create_item,
+ make_item_variant,
+ set_item_variant_settings,
+)
+from erpnext.stock.doctype.serial_no.serial_no import * # noqa
+from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
+ OpeningEntryAccountError,
+)
+from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+ create_stock_reconciliation,
+)
+from erpnext.stock.stock_ledger import get_previous_sle
+
+
def get_sle(**args):
condition, values = "", []
for key, value in iteritems(args):
@@ -539,8 +551,9 @@
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
def test_work_order(self):
- from erpnext.manufacturing.doctype.work_order.work_order \
- import make_stock_entry as _make_stock_entry
+ 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"])
@@ -618,8 +631,8 @@
s2.cancel()
def test_retain_sample(self):
- from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.doctype.batch.batch import get_batch_qty
+ 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")
@@ -824,6 +837,39 @@
frappe.db.set_default("allow_negative_stock", 0)
+ 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")
+ ],
+ )
+
+ se.distribute_additional_costs()
+
+ distributed_costs = [d.additional_cost for d in se.items]
+ self.assertEqual([0.0, 100.0, 0.0], distributed_costs)
+
+ 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")
+ ],
+ )
+
+ se.distribute_additional_costs()
+
+ distributed_costs = [d.additional_cost for d in se.items]
+ self.assertEqual([40.0, 60.0], distributed_costs)
+
def make_serialized_item(**args):
args = frappe._dict(args)
se = frappe.copy_doc(test_records[0])
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js
new file mode 100644
index 0000000..e51c90c
--- /dev/null
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js
@@ -0,0 +1,26 @@
+QUnit.module('Stock');
+
+QUnit.test("test manufacture from bom", function(assert) {
+ assert.expect(2);
+ let done = assert.async();
+ frappe.run_serially([
+ () => {
+ return frappe.tests.make("Stock Entry", [
+ { purpose: "Manufacture" },
+ { from_bom: 1 },
+ { bom_no: "BOM-_Test Item - Non Whole UOM-001" },
+ { fg_completed_qty: 2 }
+ ]);
+ },
+ () => cur_frm.save(),
+ () => frappe.click_button("Update Rate and Availability"),
+ () => {
+ assert.ok(cur_frm.doc.items[1] === 0.75, " Finished Item Qty correct");
+ assert.ok(cur_frm.doc.items[2] === 0.25, " Process Loss Item Qty correct");
+ },
+ () => frappe.tests.click_button('Submit'),
+ () => frappe.tests.click_button('Yes'),
+ () => frappe.timeout(0.3),
+ () => done()
+ ]);
+});
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
index 3cf4861..a87a7fb 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js
@@ -28,4 +28,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
index aac09c3..cae318d 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js
@@ -32,4 +32,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
index 828738e..ef0286f 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js
@@ -29,4 +29,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js
index ffd0664..54e1ac8 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js
@@ -32,4 +32,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
index cdeb4ab..fac0b4b 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js
@@ -31,4 +31,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js
index e8b2973..9f85307 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js
@@ -31,4 +31,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
index 699634d..20f119a 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
@@ -39,4 +39,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
index 770f886..8243426 100644
--- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
+++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js
@@ -31,4 +31,3 @@
() => done()
]);
});
-
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 22f412a..df65706 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -19,6 +19,7 @@
"is_finished_item",
"is_scrap_item",
"quality_inspection",
+ "is_process_loss",
"subcontracted_item",
"section_break_8",
"description",
@@ -543,13 +544,19 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_process_loss",
+ "fieldtype": "Check",
+ "label": "Is Process Loss"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-21 16:03:18.834880",
+ "modified": "2021-06-22 16:47:11.268975",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
index f9e062f..6c03425 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class StockEntryDetail(Document):
- pass
\ No newline at end of file
+ pass
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 1069ec8..3d6e264 100644
--- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
+++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
@@ -3,9 +3,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class StockEntryType(Document):
def validate(self):
if self.add_to_transit and self.purpose != 'Material Transfer':
diff --git a/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py
index 4fa73fd..7eea8f5 100644
--- a/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py
+++ b/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestStockEntryType(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index 2463a21..40ae340 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -142,6 +142,7 @@
"oldfieldtype": "Data",
"print_width": "150px",
"read_only": 1,
+ "search_index": 1,
"width": "150px"
},
{
@@ -316,7 +317,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-09-07 11:10:35.318872",
+ "modified": "2021-10-08 12:42:51.857631",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
@@ -338,4 +339,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index be1f00e..382fdfa 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -3,14 +3,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
+from datetime import date
+
import frappe
from frappe import _
-from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, cint
-from frappe.model.document import Document
-from datetime import date
-from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
-from erpnext.accounts.utils import get_fiscal_year
from frappe.core.doctype.role.role import get_users
+from frappe.model.document import Document
+from frappe.utils import add_days, cint, flt, formatdate, get_datetime, getdate
+
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
+
class StockFreezeError(frappe.ValidationError): pass
class BackDatedStockTransaction(frappe.ValidationError): pass
@@ -27,7 +31,7 @@
def validate(self):
self.flags.ignore_submit_comment = True
- from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
+ from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company
self.validate_mandatory()
self.validate_item()
self.validate_batch()
@@ -177,4 +181,3 @@
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
- frappe.db.add_index("Stock Ledger Entry", ["voucher_detail_no"])
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 af2ada8..61bae49 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -3,19 +3,25 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
-from frappe.utils import today, add_days
-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
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.stock.stock_ledger import get_previous_sle
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
-from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import create_landed_cost_voucher
-from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
-from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import BackDatedStockTransaction
+
+import frappe
from frappe.core.page.permission_manager.permission_manager import reset
+from frappe.utils import add_days, today
+
+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,
+)
+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_ledger_entry.stock_ledger_entry import BackDatedStockTransaction
+from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+ create_stock_reconciliation,
+)
+from erpnext.stock.stock_ledger import get_previous_sle
+
class TestStockLedgerEntry(unittest.TestCase):
def setUp(self):
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index cda7c1d..f59a4e6 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -2,15 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-import frappe.defaults
-from frappe import msgprint, _
-from frappe.utils import cstr, flt, cint
-from erpnext.controllers.stock_controller import StockController
+
+import frappe
+from frappe import _, msgprint
+from frappe.utils import cint, cstr, flt
+
+import erpnext
from erpnext.accounts.utils import get_company_default
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos
+from erpnext.controllers.stock_controller import StockController
from erpnext.stock.doctype.batch.batch import get_batch_qty
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.utils import get_stock_balance
+
class OpeningEntryAccountError(frappe.ValidationError): pass
class EmptyStockReconciliationItemsError(frappe.ValidationError): pass
@@ -159,8 +162,11 @@
raise frappe.ValidationError(self.validation_messages)
def validate_item(self, item_code, row):
- from erpnext.stock.doctype.item.item import validate_end_of_life, \
- validate_is_stock_item, validate_cancelled_item
+ from erpnext.stock.doctype.item.item import (
+ validate_cancelled_item,
+ validate_end_of_life,
+ validate_is_stock_item,
+ )
# using try except to catch all validation msgs and display together
@@ -390,7 +396,7 @@
sl_entries = self.merge_similar_item_serial_nos(sl_entries)
sl_entries.reverse()
- allow_negative_stock = frappe.db.get_value("Stock Settings", None, "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)
@@ -613,6 +619,11 @@
item_dict = frappe.db.get_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,
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
index 80001d6..666d2c7 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js
@@ -29,4 +29,3 @@
() => done()
]);
});
-
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index c192582..8647bee 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -5,16 +5,24 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, unittest
-from frappe.utils import flt, nowdate, nowtime, random_string, add_days
+
+import unittest
+
+import frappe
+from frappe.utils import add_days, flt, nowdate, nowtime, random_string
+
from erpnext.accounts.utils import get_stock_and_account_balance
-from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
-from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
-from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
+ EmptyStockReconciliationItemsError,
+ get_items,
+)
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
+from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
+from erpnext.tests.utils import change_settings
class TestStockReconciliation(unittest.TestCase):
@@ -310,6 +318,7 @@
pr2.cancel()
pr1.cancel()
+ @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.
@@ -320,15 +329,13 @@
SR3 | Reco | 0 | 1 (posting date: today-1) [backdated & blocked]
DN2 | DN | -2 | 8(-1) (posting date: today)
"""
- from erpnext.stock.stock_ledger import NegativeStockError
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"
warehouse = "_Test Warehouse - _TC"
create_item(item_code)
- negative_stock_setting = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
- frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 0)
pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
posting_date=add_days(nowdate(), -2))
@@ -348,11 +355,50 @@
self.assertRaises(NegativeStockError, sr3.submit)
# teardown
- frappe.db.set_value("Stock Settings", None, "allow_negative_stock", negative_stock_setting)
sr3.cancel()
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)
+ """
+ from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+ from erpnext.stock.stock_ledger import NegativeStockError
+ frappe.db.commit()
+
+ item_code = "Backdated-Reco-Cancellation-Item"
+ warehouse = "_Test Warehouse - _TC"
+ create_item(item_code)
+
+
+ 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_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
+ self.assertRaises(NegativeStockError, sr.cancel)
+
+ repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
+ self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
+
+ # teardown
+ frappe.db.rollback()
+
+
def test_valid_batch(self):
create_batch_item_with_batch("Testing Batch Item 1", "001")
create_batch_item_with_batch("Testing Batch Item 2", "002")
@@ -458,4 +504,3 @@
}, allow_negative_stock=1)
test_dependencies = ["Item", "Warehouse"]
-
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
index cc1e19d..227e727 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
@@ -2,8 +2,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class StockReconciliationItem(Document):
pass
diff --git a/erpnext/healthcare/doctype/healthcare_settings/__init__.py b/erpnext/stock/doctype/stock_reposting_settings/__init__.py
similarity index 100%
rename from erpnext/healthcare/doctype/healthcare_settings/__init__.py
rename to erpnext/stock/doctype/stock_reposting_settings/__init__.py
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js
new file mode 100644
index 0000000..42d0723
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Stock Reposting Settings', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
new file mode 100644
index 0000000..2474059
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -0,0 +1,72 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-10-01 10:56:30.814787",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "scheduling_section",
+ "limit_reposting_timeslot",
+ "start_time",
+ "end_time",
+ "limits_dont_apply_on"
+ ],
+ "fields": [
+ {
+ "fieldname": "scheduling_section",
+ "fieldtype": "Section Break",
+ "label": "Scheduling"
+ },
+ {
+ "depends_on": "limit_reposting_timeslot",
+ "fieldname": "start_time",
+ "fieldtype": "Time",
+ "label": "Start Time",
+ "mandatory_depends_on": "limit_reposting_timeslot"
+ },
+ {
+ "depends_on": "limit_reposting_timeslot",
+ "fieldname": "end_time",
+ "fieldtype": "Time",
+ "label": "End Time",
+ "mandatory_depends_on": "limit_reposting_timeslot"
+ },
+ {
+ "depends_on": "limit_reposting_timeslot",
+ "fieldname": "limits_dont_apply_on",
+ "fieldtype": "Select",
+ "label": "Limits don't apply on",
+ "options": "\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday"
+ },
+ {
+ "default": "0",
+ "fieldname": "limit_reposting_timeslot",
+ "fieldtype": "Check",
+ "label": "Limit timeslot for Stock Reposting"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-10-01 11:27:28.981594",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Reposting Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "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
new file mode 100644
index 0000000..bab521d
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from frappe.model.document import Document
+from frappe.utils import add_to_date, get_datetime, get_time_str, time_diff_in_hours
+
+
+class StockRepostingSettings(Document):
+
+
+ def validate(self):
+ self.set_minimum_reposting_time_slot()
+
+ def set_minimum_reposting_time_slot(self):
+ """Ensure that timeslot for reposting is at least 12 hours."""
+ if not self.limit_reposting_timeslot:
+ return
+
+ start_time = get_datetime(self.start_time)
+ end_time = get_datetime(self.end_time)
+
+ if start_time > end_time:
+ end_time = add_to_date(end_time, days=1, as_datetime=True)
+
+ diff = time_diff_in_hours(end_time, start_time)
+
+ if diff < 10:
+ self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True))
diff --git a/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
new file mode 100644
index 0000000..fad74d3
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestStockRepostingSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js
index 48624e0..cc0e2cf 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.js
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.js
@@ -15,37 +15,3 @@
frm.set_query("sample_retention_warehouse", filters);
}
});
-
-frappe.tour['Stock Settings'] = [
- {
- fieldname: "item_naming_by",
- title: __("Item Naming By"),
- description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
- },
- {
- fieldname: "default_warehouse",
- title: __("Default Warehouse"),
- description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master.")
- },
- {
- fieldname: "allow_negative_stock",
- title: __("Allow Negative Stock"),
- description: __("This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.")
-
- },
- {
- fieldname: "valuation_method",
- title: __("Valuation Method"),
- description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/stock/articles/item-valuation-fifo-and-moving-average' target='_blank'>here</a>" + __(" to know more about them.")
- },
- {
- fieldname: "show_barcode_field",
- title: __("Show Barcode Field"),
- description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.")
- },
- {
- fieldname: "automatically_set_serial_nos_based_on_fifo",
- title: __("Automatically Set Serial Nos based on FIFO"),
- description: __("Serial numbers for stock will be set automatically based on the Items entered based on first in first out in transactions like Purchase/Sales Invoices, Delivery Notes, etc.")
- }
-];
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 2dd7c6f..2a634b3 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -4,12 +4,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.model.document import Document
-from frappe.utils.html_utils import clean_html
-from frappe.utils import cint
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+from frappe.model.document import Document
+from frappe.utils import cint
+from frappe.utils.html_utils import clean_html
+
class StockSettings(Document):
def validate(self):
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.js b/erpnext/stock/doctype/stock_settings/test_stock_settings.js
deleted file mode 100644
index 57d9fc6..0000000
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Stock Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Stock Settings
- () => frappe.tests.make('Stock Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.py b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
index 42a78f7..7e80904 100644
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestStockSettings(unittest.TestCase):
def setUp(self):
frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
diff --git a/erpnext/stock/doctype/uom_category/test_uom_category.js b/erpnext/stock/doctype/uom_category/test_uom_category.js
deleted file mode 100644
index 4b5972e..0000000
--- a/erpnext/stock/doctype/uom_category/test_uom_category.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: UOM Category", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new UOM Category
- () => frappe.tests.make('UOM Category', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/uom_category/test_uom_category.py b/erpnext/stock/doctype/uom_category/test_uom_category.py
index 33bd408..dd5510a 100644
--- a/erpnext/stock/doctype/uom_category/test_uom_category.py
+++ b/erpnext/stock/doctype/uom_category/test_uom_category.py
@@ -5,5 +5,6 @@
import unittest
+
class TestUOMCategory(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/uom_category/uom_category.py b/erpnext/stock/doctype/uom_category/uom_category.py
index d5c339e..282ebb2 100644
--- a/erpnext/stock/doctype/uom_category/uom_category.py
+++ b/erpnext/stock/doctype/uom_category/uom_category.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class UOMCategory(Document):
pass
diff --git a/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py b/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py
index 67fe20b..9d9d4c6 100644
--- a/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py
+++ b/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class UOMConversionDetail(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/stock/doctype/variant_field/test_variant_field.js b/erpnext/stock/doctype/variant_field/test_variant_field.js
deleted file mode 100644
index 2600a10..0000000
--- a/erpnext/stock/doctype/variant_field/test_variant_field.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Variant Field", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Variant Field
- () => frappe.tests.make('Variant Field', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/stock/doctype/variant_field/test_variant_field.py b/erpnext/stock/doctype/variant_field/test_variant_field.py
index 53024bd..408e33b 100644
--- a/erpnext/stock/doctype/variant_field/test_variant_field.py
+++ b/erpnext/stock/doctype/variant_field/test_variant_field.py
@@ -5,5 +5,6 @@
import unittest
+
class TestVariantField(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/variant_field/variant_field.py b/erpnext/stock/doctype/variant_field/variant_field.py
index a77301e..abcfdc7 100644
--- a/erpnext/stock/doctype/variant_field/variant_field.py
+++ b/erpnext/stock/doctype/variant_field/variant_field.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class VariantField(Document):
pass
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.js b/erpnext/stock/doctype/warehouse/test_warehouse.js
index 8ea280c..850da1e 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.js
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.js
@@ -16,4 +16,4 @@
() => done()
]);
-});
\ 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 e3981c9..1ca7181 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -5,13 +5,13 @@
import unittest
import frappe
-from frappe.utils import cint
from frappe.test_runner import make_test_records
+from frappe.utils import cint
import erpnext
-from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
+from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
test_records = frappe.get_test_records('Warehouse')
@@ -180,4 +180,4 @@
if not company_abbr:
company_abbr = frappe.get_cached_value("Company", company, 'abbr')
group_stock_account = "Current Assets - " + company_abbr
- return group_stock_account
\ No newline at end of file
+ return group_stock_account
diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js
index 1f17250..9243e1e 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.js
+++ b/erpnext/stock/doctype/warehouse/warehouse.js
@@ -48,11 +48,11 @@
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: {
@@ -83,6 +83,6 @@
callback: function(){
frm.refresh();
}
-
+
})
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index 3abc139..ecd8707 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -2,13 +2,18 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import cint, flt
-from frappe import throw, _
+
from collections import defaultdict
-from frappe.utils.nestedset import NestedSet
-from erpnext.stock import get_warehouse_account
+
+import frappe
+from frappe import _, throw
from frappe.contacts.address_and_contact import load_address_and_contact
+from frappe.utils import cint, flt
+from frappe.utils.nestedset import NestedSet
+
+import erpnext
+from erpnext.stock import get_warehouse_account
+
class Warehouse(NestedSet):
nsm_parent_field = 'parent_warehouse'
diff --git a/erpnext/stock/doctype/warehouse/warehouse_tree.js b/erpnext/stock/doctype/warehouse/warehouse_tree.js
index 407d7d1..e9e14c7 100644
--- a/erpnext/stock/doctype/warehouse/warehouse_tree.js
+++ b/erpnext/stock/doctype/warehouse/warehouse_tree.js
@@ -24,4 +24,4 @@
+ '</span>').insertBefore(node.$ul);
}
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py b/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
index 39f4b23..846e63b 100644
--- a/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
+++ b/erpnext/stock/doctype/warehouse_type/test_warehouse_type.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestWarehouseType(unittest.TestCase):
pass
diff --git a/erpnext/stock/doctype/warehouse_type/warehouse_type.py b/erpnext/stock/doctype/warehouse_type/warehouse_type.py
index 4b7d8d8..fd83d78 100644
--- a/erpnext/stock/doctype/warehouse_type/warehouse_type.py
+++ b/erpnext/stock/doctype/warehouse_type/warehouse_type.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class WarehouseType(Document):
pass
diff --git a/erpnext/stock/form_tour/item/item.json b/erpnext/stock/form_tour/item/item.json
new file mode 100644
index 0000000..821e91b
--- /dev/null
+++ b/erpnext/stock/form_tour/item/item.json
@@ -0,0 +1,89 @@
+{
+ "creation": "2021-08-24 17:56:40.754909",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 18:04:50.928431",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item",
+ "owner": "Administrator",
+ "reference_doctype": "Item",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "Enter code for Asset Item",
+ "field": "",
+ "fieldname": "item_code",
+ "fieldtype": "Data",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Item Code",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Asset Item Code"
+ },
+ {
+ "description": "Enter name for Asset Item",
+ "field": "",
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Item Name",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Asset Item Name"
+ },
+ {
+ "description": "Check this field to make this an Asset Item",
+ "field": "",
+ "fieldname": "is_fixed_asset",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Is Fixed Asset",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Is this a Fixed Asset?"
+ },
+ {
+ "description": "On checking it, the system will create an Asset automatically on purchase",
+ "field": "",
+ "fieldname": "auto_create_assets",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Auto Create Assets on Purchase",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Auto Create Asset on Purchase"
+ },
+ {
+ "description": "Select an Asset Category for this Asset Item",
+ "field": "",
+ "fieldname": "asset_category",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Asset Category",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Asset Category"
+ },
+ {
+ "description": "Select a naming series which will be used to create an Asset automatically",
+ "field": "",
+ "fieldname": "asset_naming_series",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Asset Naming Series",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Asset Naming Series"
+ }
+ ],
+ "title": "Item"
+}
diff --git a/erpnext/stock/form_tour/purchase_receipt/purchase_receipt.json b/erpnext/stock/form_tour/purchase_receipt/purchase_receipt.json
new file mode 100644
index 0000000..6fba3f4
--- /dev/null
+++ b/erpnext/stock/form_tour/purchase_receipt/purchase_receipt.json
@@ -0,0 +1,41 @@
+{
+ "creation": "2021-08-24 13:03:21.333088",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 13:03:21.333088",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Purchase Receipt",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Receipt",
+ "save_on_complete": 0,
+ "steps": [
+ {
+ "description": "Select Asset Supplier",
+ "field": "",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Supplier",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Supplier"
+ },
+ {
+ "description": "Select an Asset Item, Enter rate and quantity",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Items",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Items"
+ }
+ ],
+ "title": "Purchase Receipt"
+}
\ No newline at end of file
diff --git a/erpnext/stock/form_tour/stock_entry/stock_entry.json b/erpnext/stock/form_tour/stock_entry/stock_entry.json
new file mode 100644
index 0000000..6363c6a
--- /dev/null
+++ b/erpnext/stock/form_tour/stock_entry/stock_entry.json
@@ -0,0 +1,56 @@
+{
+ "creation": "2021-08-24 14:44:22.292652",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-25 16:31:31.441194",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Entry",
+ "owner": "Administrator",
+ "reference_doctype": "Stock Entry",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "Select the type of Stock Entry to be made. For now, to receive stock into a warehouses select <a href=\"https://docs.erpnext.com/docs/v13/user/manual/en/stock/articles/stock-entry-purpose#2purpose-material-receipt\" target=\"_blank\">Material Receipt.</a>",
+ "field": "",
+ "fieldname": "stock_entry_type",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Stock Entry Type",
+ "next_step_condition": "eval: doc.stock_entry_type === \"Material Receipt\"",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Stock Entry Type"
+ },
+ {
+ "description": "Select a target warehouse where the stock will be received.",
+ "field": "",
+ "fieldname": "to_warehouse",
+ "fieldtype": "Link",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Default Target Warehouse",
+ "next_step_condition": "eval: doc.to_warehouse",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Default Target Warehouse"
+ },
+ {
+ "description": "Select an item and entry quantity to be delivered.",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Items",
+ "next_step_condition": "eval: doc.items[0]?.item_code",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Items"
+ }
+ ],
+ "title": "Stock Entry"
+}
\ No newline at end of file
diff --git a/erpnext/stock/form_tour/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/form_tour/stock_reconciliation/stock_reconciliation.json
new file mode 100644
index 0000000..5b7fd72
--- /dev/null
+++ b/erpnext/stock/form_tour/stock_reconciliation/stock_reconciliation.json
@@ -0,0 +1,55 @@
+{
+ "creation": "2021-08-24 14:44:46.770952",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-25 16:26:11.718664",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Reconciliation",
+ "owner": "Administrator",
+ "reference_doctype": "Stock Reconciliation",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "Set Purpose to Opening Stock to set the stock opening balance.",
+ "field": "",
+ "fieldname": "purpose",
+ "fieldtype": "Select",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Purpose",
+ "next_step_condition": "eval: doc.purpose === \"Opening Stock\"",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Purpose"
+ },
+ {
+ "description": "Select the items for which the opening stock has to be set.",
+ "field": "",
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Items",
+ "next_step_condition": "eval: doc.items[0]?.item_code",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Items"
+ },
+ {
+ "description": "Edit the Posting Date by clicking on the Edit Posting Date and Time checkbox below.",
+ "field": "",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Posting Date",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Posting Date"
+ }
+ ],
+ "title": "Stock Reconciliation"
+}
\ No newline at end of file
diff --git a/erpnext/stock/form_tour/stock_settings/stock_settings.json b/erpnext/stock/form_tour/stock_settings/stock_settings.json
new file mode 100644
index 0000000..3d164e3
--- /dev/null
+++ b/erpnext/stock/form_tour/stock_settings/stock_settings.json
@@ -0,0 +1,89 @@
+{
+ "creation": "2021-08-20 15:20:59.336585",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-25 16:19:37.699528",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Stock Settings",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a Naming Series choose the 'Naming Series' option.",
+ "field": "",
+ "fieldname": "item_naming_by",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Item Naming By",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Item Naming By"
+ },
+ {
+ "description": "Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master.",
+ "field": "",
+ "fieldname": "default_warehouse",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Default Warehouse",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Default Warehouse"
+ },
+ {
+ "description": "Quality inspection is performed on the inward and outward movement of goods. Receipt and delivery transactions will be stopped or the user will be warned if the quality inspection is not performed.",
+ "field": "",
+ "fieldname": "action_if_quality_inspection_is_not_submitted",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Action If Quality Inspection Is Not Submitted",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Action if Quality Inspection Is Not Submitted"
+ },
+ {
+ "description": "Serial numbers for stock will be set automatically based on the Items entered based on first in first out in transactions like Purchase/Sales Invoices, Delivery Notes, etc.",
+ "field": "",
+ "fieldname": "automatically_set_serial_nos_based_on_fifo",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Automatically Set Serial Nos Based on FIFO",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Automatically Set Serial Nos based on FIFO"
+ },
+ {
+ "description": "Show 'Scan Barcode' field above every child table to insert Items with ease.",
+ "field": "",
+ "fieldname": "show_barcode_field",
+ "fieldtype": "Check",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Show Barcode Field in Stock Transactions",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Show Barcode Field"
+ },
+ {
+ "description": "Choose between FIFO and Moving Average Valuation Methods. Click <a href=\"https://docs.erpnext.com/docs/user/manual/en/stock/articles/item-valuation-fifo-and-moving-average\" target=\"_blank\">here</a> to know more about them.",
+ "field": "",
+ "fieldname": "valuation_method",
+ "fieldtype": "Select",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Default Valuation Method",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Default Valuation Method"
+ }
+ ],
+ "title": "Stock Settings"
+}
\ No newline at end of file
diff --git a/erpnext/stock/form_tour/warehouse/warehouse.json b/erpnext/stock/form_tour/warehouse/warehouse.json
new file mode 100644
index 0000000..23ff2ae
--- /dev/null
+++ b/erpnext/stock/form_tour/warehouse/warehouse.json
@@ -0,0 +1,54 @@
+{
+ "creation": "2021-08-24 14:43:44.465237",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-08-24 14:50:31.988256",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Warehouse",
+ "owner": "Administrator",
+ "reference_doctype": "Warehouse",
+ "save_on_complete": 1,
+ "steps": [
+ {
+ "description": "Select a name for the warehouse. This should reflect its location or purpose.",
+ "field": "",
+ "fieldname": "warehouse_name",
+ "fieldtype": "Data",
+ "has_next_condition": 1,
+ "is_table_field": 0,
+ "label": "Warehouse Name",
+ "next_step_condition": "eval: doc.warehouse_name",
+ "parent_field": "",
+ "position": "Bottom",
+ "title": "Warehouse Name"
+ },
+ {
+ "description": "Select a warehouse type to categorize the warehouse into a sub-group.",
+ "field": "",
+ "fieldname": "warehouse_type",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Warehouse Type",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Warehouse Type"
+ },
+ {
+ "description": "Select an account to set a default account for all transactions with this warehouse.",
+ "field": "",
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "has_next_condition": 0,
+ "is_table_field": 0,
+ "label": "Account",
+ "parent_field": "",
+ "position": "Top",
+ "title": "Account"
+ }
+ ],
+ "title": "Warehouse"
+}
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index a0fbcec..cbff214 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -2,22 +2,27 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
+import json
+
import frappe
from frappe import _, throw
-from frappe.utils import flt, cint, add_days, cstr, add_months, getdate
-import json, copy
-from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item, set_transaction_type
-from erpnext.setup.utils import get_exchange_rate
from frappe.model.meta import get_field_precision
-from erpnext.stock.doctype.batch.batch import get_batch_no
-from erpnext import get_company_currency
-from erpnext.stock.doctype.item.item import get_item_defaults, get_uom_conv_factor
-from erpnext.stock.doctype.price_list.price_list import get_price_list_details
-from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
-from erpnext.setup.doctype.brand.brand import get_brand_defaults
-from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_manufacturer_part_no
+from frappe.utils import add_days, add_months, cint, cstr, flt, getdate
+from six import iteritems, string_types
-from six import string_types, iteritems
+from erpnext import get_company_currency
+from erpnext.accounts.doctype.pricing_rule.pricing_rule import (
+ get_pricing_rule_for_item,
+ set_transaction_type,
+)
+from erpnext.setup.doctype.brand.brand import get_brand_defaults
+from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.stock.doctype.batch.batch import get_batch_no
+from erpnext.stock.doctype.item.item import get_item_defaults, get_uom_conv_factor
+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']
@@ -278,6 +283,10 @@
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'] = ''
+
out = frappe._dict({
"item_code": item.name,
"item_name": item.item_name,
@@ -312,8 +321,8 @@
"transaction_date": args.get("transaction_date"),
"against_blanket_order": args.get("against_blanket_order"),
"bom_no": item.get("default_bom"),
- "weight_per_unit": item.get("weight_per_unit"),
- "weight_uom": item.get("weight_uom")
+ "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")
})
if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
@@ -373,7 +382,7 @@
return out
-def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
+def get_item_warehouse(item, args, overwrite_warehouse, defaults=None):
if not defaults:
defaults = frappe._dict({
'item_defaults' : get_item_defaults(item.name, args.company),
diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js
index f3f6196..ff8a69f 100644
--- a/erpnext/stock/landed_taxes_and_charges_common.js
+++ b/erpnext/stock/landed_taxes_and_charges_common.js
@@ -59,4 +59,3 @@
}
});
});
-
diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json
index 8474648..c246747 100644
--- a/erpnext/stock/module_onboarding/stock/stock.json
+++ b/erpnext/stock/module_onboarding/stock/stock.json
@@ -19,32 +19,26 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock",
"idx": 0,
"is_complete": 0,
- "modified": "2020-10-14 14:54:42.741971",
+ "modified": "2021-08-20 14:38:55.570067",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock",
"owner": "Administrator",
"steps": [
{
- "step": "Setup your Warehouse"
+ "step": "Stock Settings"
},
{
- "step": "Create a Product"
- },
- {
- "step": "Create a Supplier"
- },
- {
- "step": "Introduction to Stock Entry"
+ "step": "Create a Warehouse"
},
{
"step": "Create a Stock Entry"
},
{
- "step": "Create a Purchase Receipt"
+ "step": "Stock Opening Balance"
},
{
- "step": "Stock Settings"
+ "step": "View Stock Projected Qty"
}
],
"subtitle": "Inventory, Warehouses, Analysis, and more.",
diff --git a/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json
deleted file mode 100644
index 9012493..0000000
--- a/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "action": "Create Entry",
- "creation": "2020-05-19 18:59:13.266713",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2020-10-14 14:53:25.618434",
- "modified_by": "Administrator",
- "name": "Create a Purchase Receipt",
- "owner": "Administrator",
- "reference_document": "Purchase Receipt",
- "show_full_form": 1,
- "title": "Create a Purchase Receipt",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json
index 09902b8..3cb522c 100644
--- a/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json
+++ b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json
@@ -1,19 +1,21 @@
{
"action": "Create Entry",
+ "action_label": "Create a Material Transfer Entry",
"creation": "2020-05-15 03:20:16.277043",
+ "description": "# Manage Stock Movements\nStock entry allows you to register the movement of stock for various purposes like transfer, received, issues, repacked, etc. To address issues related to theft and pilferages, you can always ensure that the movement of goods happens against a document reference Stock Entry in ERPNext.\n\nLet\u2019s get a quick walk-through on the various scenarios covered in Stock Entry by watching [*this video*](https://www.youtube.com/watch?v=Njt107hlY3I).",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-14 14:53:00.105905",
+ "modified": "2021-06-18 13:57:11.434063",
"modified_by": "Administrator",
"name": "Create a Stock Entry",
"owner": "Administrator",
"reference_document": "Stock Entry",
+ "show_form_tour": 1,
"show_full_form": 1,
- "title": "Create a Stock Entry",
+ "title": "Manage Stock Movements",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json
index ef61fa3..49efe57 100644
--- a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json
+++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json
@@ -1,18 +1,19 @@
{
- "action": "Create Entry",
+ "action": "Show Form Tour",
"creation": "2020-05-14 22:09:10.043554",
+ "description": "# Create a Supplier\nIn this step we will create a **Supplier**. If you have already created a **Supplier** you can skip this step.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-14 14:53:00.120455",
+ "modified": "2021-05-17 16:37:37.697077",
"modified_by": "Administrator",
"name": "Create a Supplier",
"owner": "Administrator",
"reference_document": "Supplier",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Supplier",
"validate_action": 1
diff --git a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json
new file mode 100644
index 0000000..22c88bf
--- /dev/null
+++ b/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json
@@ -0,0 +1,21 @@
+{
+ "action": "Create Entry",
+ "action_label": "Let\u2019s create your first warehouse ",
+ "creation": "2021-05-17 16:13:19.297789",
+ "description": "# Setup a Warehouse\nThe warehouse can be your location/godown/store where you maintain the item's inventory, and receive/deliver them to various parties.\n\nIn ERPNext, you can maintain a Warehouse in the tree structure, so that location and sub-location of an item can be tracked. Also, you can link a Warehouse to a specific Accounting ledger, where the real-time stock value of that warehouse\u2019s item will be reflected.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-18 12:23:36.675572",
+ "modified_by": "Administrator",
+ "name": "Create a Warehouse",
+ "owner": "Administrator",
+ "reference_document": "Warehouse",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Setup a Warehouse",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/create_an_item/create_an_item.json b/erpnext/stock/onboarding_step/create_an_item/create_an_item.json
new file mode 100644
index 0000000..016cbd5
--- /dev/null
+++ b/erpnext/stock/onboarding_step/create_an_item/create_an_item.json
@@ -0,0 +1,22 @@
+{
+ "action": "Create Entry",
+ "action_label": "",
+ "creation": "2021-05-17 13:47:18.515052",
+ "description": "# Create an Item\nThe Stock module deals with the movement of items.\n\nIn this step we will create an [**Item**](https://docs.erpnext.com/docs/user/manual/en/stock/item).",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "intro_video_url": "",
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-05-18 16:15:20.695028",
+ "modified_by": "Administrator",
+ "name": "Create an Item",
+ "owner": "Administrator",
+ "reference_document": "Item",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Create an Item",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json
index 212e505..384950e 100644
--- a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json
+++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json
@@ -1,17 +1,18 @@
{
"action": "Watch Video",
"creation": "2020-05-15 02:47:17.958806",
+ "description": "# Introduction to Stock Entry\nThis video will give a quick introduction to [**Stock Entry**](https://docs.erpnext.com/docs/user/manual/en/stock/stock-entry).",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-14 14:53:00.075177",
+ "modified": "2021-05-18 15:13:43.306064",
"modified_by": "Administrator",
"name": "Introduction to Stock Entry",
"owner": "Administrator",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Introduction to Stock Entry",
"validate_action": 1,
diff --git a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
index 75940ed..5d33a64 100644
--- a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
+++ b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json
@@ -5,15 +5,15 @@
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2020-10-14 14:53:25.538900",
+ "modified": "2021-05-17 13:53:06.936579",
"modified_by": "Administrator",
"name": "Setup your Warehouse",
"owner": "Administrator",
"path": "Tree/Warehouse",
"reference_document": "Warehouse",
+ "show_form_tour": 0,
"show_full_form": 0,
"title": "Set up your Warehouse",
"validate_action": 1
diff --git a/erpnext/stock/onboarding_step/stock_opening_balance/stock_opening_balance.json b/erpnext/stock/onboarding_step/stock_opening_balance/stock_opening_balance.json
new file mode 100644
index 0000000..48fd1fd
--- /dev/null
+++ b/erpnext/stock/onboarding_step/stock_opening_balance/stock_opening_balance.json
@@ -0,0 +1,22 @@
+{
+ "action": "Create Entry",
+ "action_label": "Let\u2019s create a stock opening entry",
+ "creation": "2021-05-17 16:13:47.511883",
+ "description": "# Update Stock Opening Balance\nIt\u2019s an entry to update the stock balance of an item, in a warehouse, on a date and time you are going live on ERPNext.\n\nOnce opening stocks are updated, you can create transactions like manufacturing and stock deliveries, where this opening stock will be consumed.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-06-18 13:59:36.021097",
+ "modified_by": "Administrator",
+ "name": "Stock Opening Balance",
+ "owner": "Administrator",
+ "reference_document": "Stock Reconciliation",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Update Stock Opening Balance",
+ "validate_action": 1,
+ "video_url": "https://www.youtube.com/watch?v=nlHX0ZZ84Lw"
+}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/stock_settings/stock_settings.json b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json
index ae34afa..2cf90e8 100644
--- a/erpnext/stock/onboarding_step/stock_settings/stock_settings.json
+++ b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json
@@ -1,19 +1,21 @@
{
"action": "Show Form Tour",
+ "action_label": "Take a walk through Stock Settings",
"creation": "2020-05-15 02:53:57.209967",
+ "description": "# Review Stock Settings\n\nIn ERPNext, the Stock module\u2019s features are configurable as per your business needs. Stock Settings is the place where you can set your preferences for:\n- Default values for Item and Pricing\n- Default valuation method for inventory valuation\n- Set preference for serialization and batching of item\n- Set tolerance for over-receipt and delivery of items",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
- "is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
- "modified": "2020-10-14 14:53:00.092504",
+ "modified": "2021-08-18 12:06:51.139387",
"modified_by": "Administrator",
"name": "Stock Settings",
"owner": "Administrator",
"reference_document": "Stock Settings",
+ "show_form_tour": 0,
"show_full_form": 0,
- "title": "Explore Stock Settings",
+ "title": "Review Stock Settings",
"validate_action": 1
}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/view_stock_projected_qty/view_stock_projected_qty.json b/erpnext/stock/onboarding_step/view_stock_projected_qty/view_stock_projected_qty.json
new file mode 100644
index 0000000..e684780
--- /dev/null
+++ b/erpnext/stock/onboarding_step/view_stock_projected_qty/view_stock_projected_qty.json
@@ -0,0 +1,24 @@
+{
+ "action": "View Report",
+ "action_label": "Check Stock Projected Qty",
+ "creation": "2021-08-20 14:38:41.649103",
+ "description": "# Check Stock Reports\nBased on the various stock transactions, you can get a host of one-click Stock Reports in ERPNext like Stock Ledger, Stock Balance, Projected Quantity, and Ageing analysis.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-08-20 14:38:41.649103",
+ "modified_by": "Administrator",
+ "name": "View Stock Projected Qty",
+ "owner": "Administrator",
+ "reference_report": "Stock Projected Qty",
+ "report_description": "You can set the filters to narrow the results, then click on Generate New Report to see the updated report.",
+ "report_reference_doctype": "Item",
+ "report_type": "Script Report",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "Check Stock Projected Qty",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/onboarding_step/view_warehouses/view_warehouses.json b/erpnext/stock/onboarding_step/view_warehouses/view_warehouses.json
new file mode 100644
index 0000000..c46c4bd
--- /dev/null
+++ b/erpnext/stock/onboarding_step/view_warehouses/view_warehouses.json
@@ -0,0 +1,20 @@
+{
+ "action": "Go to Page",
+ "creation": "2021-05-17 16:12:43.427579",
+ "description": "# View Warehouse\nIn ERPNext the term 'warehouse' can be thought of as a storage location.\n\nWarehouses are arranged in ERPNext in a tree like structure, where multiple sub-warehouses can be grouped under a single warehouse.\n\nIn this step we will view the [**Warehouse Tree**](https://docs.erpnext.com/docs/user/manual/en/stock/warehouse#21-tree-view) to view the [**Warehouses**](https://docs.erpnext.com/docs/user/manual/en/stock/warehouse) that are set by default.",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2021-05-18 15:04:41.198413",
+ "modified_by": "Administrator",
+ "name": "View Warehouses",
+ "owner": "Administrator",
+ "path": "Tree/Warehouse",
+ "show_form_tour": 0,
+ "show_full_form": 0,
+ "title": "View Warehouses",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
index 90112c7..de7e38e 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
@@ -37,4 +37,4 @@
{% endif %}
</div>
</div>
-{% endfor %}
\ No newline at end of file
+{% endfor %}
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
index acaf180..7ac5e64 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
@@ -16,4 +16,4 @@
% Occupied
</div>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 4c721ac..7c6fbfd 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -2,11 +2,16 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-import erpnext
+
import json
-from frappe.utils import flt, nowdate, add_days, cint
+from math import ceil
+
+import frappe
from frappe import _
+from frappe.utils import add_days, cint, flt, nowdate
+
+import erpnext
+
def reorder_item():
""" Reorder item if stock reaches reorder level"""
@@ -145,11 +150,16 @@
conversion_factor = frappe.db.get_value("UOM Conversion Detail",
{'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0
+ must_be_whole_number = frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True)
+ qty = d.reorder_qty / conversion_factor
+ if must_be_whole_number:
+ qty = ceil(qty)
+
mr.append("items", {
"doctype": "Material Request Item",
"item_code": d.item_code,
"schedule_date": add_days(nowdate(),cint(item.lead_time_days)),
- "qty": d.reorder_qty / conversion_factor,
+ "qty": qty,
"uom": uom,
"stock_uom": item.stock_uom,
"warehouse": d.warehouse,
@@ -166,7 +176,7 @@
mr.submit()
mr_list.append(mr)
- except:
+ except Exception:
_log_exception()
if mr_list:
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 7354eee..da57bad 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, cint, getdate
+from frappe.utils import cint, getdate
+
def execute(filters=None):
if not filters: filters = {}
@@ -24,7 +26,7 @@
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
@@ -70,7 +72,7 @@
"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')
qty_dict.expires_on = expiry_date_unicode
diff --git a/erpnext/stock/report/bom_search/bom_search.py b/erpnext/stock/report/bom_search/bom_search.py
index e3955c9..8b583f3 100644
--- a/erpnext/stock/report/bom_search/bom_search.py
+++ b/erpnext/stock/report/bom_search/bom_search.py
@@ -2,10 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
+import frappe
from six import iteritems
+
def execute(filters=None):
data = []
parents = {
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 9e5e63e..5f6184d 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
@@ -1,8 +1,8 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-from collections import OrderedDict
import datetime
+from collections import OrderedDict
from typing import Dict, List, Tuple, Union
import frappe
@@ -11,7 +11,6 @@
from erpnext.accounts.report.general_ledger.general_ledger import get_gl_entries
-
Filters = frappe._dict
Row = frappe._dict
Data = List[Row]
@@ -43,13 +42,13 @@
def get_columns() -> Columns:
return [
{
- 'label': 'Item Group',
+ 'label': _('Item Group'),
'fieldname': 'item_group',
'fieldtype': 'Data',
'width': '200'
},
{
- 'label': 'COGS Debit',
+ 'label': _('COGS Debit'),
'fieldname': 'cogs_debit',
'fieldtype': 'Currency',
'width': '200'
@@ -64,7 +63,7 @@
assign_self_values(leveled_dict, svd_list)
assign_agg_values(leveled_dict)
-
+
data = []
for item in leveled_dict.items():
i = item[1]
@@ -160,7 +159,7 @@
if is_bold:
item_group = frappe.bold(item_group)
return frappe._dict(item_group=item_group, cogs_debit=value, indent=indent)
-
+
def assign_item_groups_to_svd_list(svd_list: SVDList) -> None:
ig_map = get_item_groups_map(svd_list)
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 4fc4027..1dd0478 100644
--- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py
+++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import date_diff
+
def execute(filters=None, consolidated = False):
data, columns = DelayedItemReport(filters).run()
@@ -174,4 +176,4 @@
"fieldname": "po_no",
"fieldtype": "Data",
"width": 100
- }]
\ No newline at end of file
+ }]
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 79dc5d8..677e30c 100644
--- a/erpnext/stock/report/delayed_order_report/delayed_order_report.py
+++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.py
@@ -2,9 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe import _
+
from erpnext.stock.report.delayed_item_report.delayed_item_report import DelayedItemReport
+
def execute(filters=None):
columns, data = [], []
@@ -87,4 +90,4 @@
"fieldname": "po_no",
"fieldtype": "Data",
"width": 110
- }]
\ No newline at end of file
+ }]
diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js
index ade004c..8a04565 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js
@@ -6,4 +6,3 @@
filters: erpnext.get_sales_trends_filters()
}
});
-
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 446d304..6d03ec1 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
-from erpnext.controllers.trends import get_columns,get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
@@ -47,4 +49,4 @@
]
},
"type" : "bar"
- }
\ No newline at end of file
+ }
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 cf174c9..cf27326 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
@@ -3,8 +3,9 @@
import frappe
from frappe import _
-from six import iteritems
from frappe.utils import flt
+from six import iteritems
+
def execute(filters=None):
columns, data = [], []
@@ -108,4 +109,4 @@
'fieldtype': 'Float',
'fieldname': 'differnce',
'width': 110
- }]
\ No newline at end of file
+ }]
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 e54cf4c..5f03c7c 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
@@ -1,12 +1,15 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-import frappe
import copy
+
+import frappe
from frappe import _
from six import iteritems
+
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
def execute(filters=None):
columns, data = [], []
columns = get_columns()
@@ -145,4 +148,4 @@
'fieldtype': 'Currency',
'fieldname': 'valuation_rate',
'width': 110
- }]
\ No newline at end of file
+ }]
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 a724387..bc520ae 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
@@ -2,13 +2,16 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
-import erpnext
from frappe import _
+from frappe.utils import add_days, getdate, today
from six import iteritems
-from frappe.utils import add_days, today, getdate
-from erpnext.stock.utils import get_stock_value_on
+
+import erpnext
from erpnext.accounts.utils import get_stock_and_account_balance
+from erpnext.stock.utils import get_stock_value_on
+
def execute(filters=None):
if not erpnext.is_perpetual_inventory_enabled(filters.company):
@@ -138,4 +141,4 @@
"fieldtype": "Currency",
"width": "150"
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.js b/erpnext/stock/report/item_price_stock/item_price_stock.js
index 0bbc61b..7af1dab 100644
--- a/erpnext/stock/report/item_price_stock/item_price_stock.js
+++ b/erpnext/stock/report/item_price_stock/item_price_stock.js
@@ -11,4 +11,4 @@
"options": "Item"
}
]
-}
\ No newline at end of file
+}
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 db7498b..6ffb5c8 100644
--- a/erpnext/stock/report/item_price_stock/item_price_stock.py
+++ b/erpnext/stock/report/item_price_stock/item_price_stock.py
@@ -1,9 +1,11 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns, data = [], []
columns=get_columns()
diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py
index 12f3297..aa5ae0e 100644
--- a/erpnext/stock/report/item_prices/item_prices.py
+++ b/erpnext/stock/report/item_prices/item_prices.py
@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe import msgprint, _
+from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
if not filters: filters = {}
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 086d833..1438e6c 100644
--- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py
+++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns()
conditions = get_conditions(filters)
@@ -158,5 +160,3 @@
]
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 d8563d7..eedda53 100644
--- a/erpnext/stock/report/item_variant_details/item_variant_details.py
+++ b/erpnext/stock/report/item_variant_details/item_variant_details.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
columns = get_columns(filters.item)
data = get_data(filters.item)
diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js
index c0535bf..173aad6 100644
--- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js
+++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js
@@ -29,4 +29,4 @@
"options": "Brand"
}
]
-}
\ No newline at end of file
+}
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 2e13aa0..08869af 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
@@ -4,7 +4,8 @@
import frappe
from frappe import _
-from frappe.utils import getdate, flt
+from frappe.utils import flt, getdate
+
def execute(filters=None):
if not filters: filters = {}
diff --git a/erpnext/healthcare/report/__init__.py b/erpnext/stock/report/process_loss_report/__init__.py
similarity index 100%
rename from erpnext/healthcare/report/__init__.py
rename to erpnext/stock/report/process_loss_report/__init__.py
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.js b/erpnext/stock/report/process_loss_report/process_loss_report.js
new file mode 100644
index 0000000..b0c2b94
--- /dev/null
+++ b/erpnext/stock/report/process_loss_report/process_loss_report.js
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Process Loss Report"] = {
+ filters: [
+ {
+ label: __("Company"),
+ fieldname: "company",
+ fieldtype: "Link",
+ options: "Company",
+ mandatory: true,
+ default: frappe.defaults.get_user_default("Company"),
+ },
+ {
+ label: __("Item"),
+ fieldname: "item",
+ fieldtype: "Link",
+ options: "Item",
+ mandatory: false,
+ },
+ {
+ label: __("Work Order"),
+ fieldname: "work_order",
+ fieldtype: "Link",
+ options: "Work Order",
+ mandatory: false,
+ },
+ {
+ label: __("From Date"),
+ fieldname: "from_date",
+ fieldtype: "Date",
+ mandatory: true,
+ default: frappe.datetime.year_start(),
+ },
+ {
+ label: __("To Date"),
+ fieldname: "to_date",
+ fieldtype: "Date",
+ mandatory: true,
+ default: frappe.datetime.get_today(),
+ },
+ ]
+};
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.json b/erpnext/stock/report/process_loss_report/process_loss_report.json
new file mode 100644
index 0000000..afe4aff
--- /dev/null
+++ b/erpnext/stock/report/process_loss_report/process_loss_report.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-08-24 16:38:15.233395",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-08-24 16:38:15.233395",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Process Loss Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Work Order",
+ "report_name": "Process Loss Report",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Manufacturing User"
+ },
+ {
+ "role": "Stock User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.py b/erpnext/stock/report/process_loss_report/process_loss_report.py
new file mode 100644
index 0000000..499c49f
--- /dev/null
+++ b/erpnext/stock/report/process_loss_report/process_loss_report.py
@@ -0,0 +1,133 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from typing import Dict, List, Tuple
+
+import frappe
+from frappe import _
+
+Filters = frappe._dict
+Row = frappe._dict
+Data = List[Row]
+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': _('Item'),
+ 'fieldname': 'production_item',
+ 'fieldtype': 'Link',
+ 'options': 'Item',
+ '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': _('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)
+ )
+ return query_args
+
+def run_query(query_args: QueryArgs) -> Data:
+ return frappe.db.sql("""
+ SELECT
+ wo.name, wo.status, wo.production_item, wo.qty,
+ wo.produced_qty, wo.process_loss_qty,
+ (wo.produced_qty - wo.process_loss_qty) as actual_produced_qty,
+ sum(se.total_incoming_value) as total_fg_value,
+ sum(se.total_outgoing_value) as total_rm_value
+ FROM
+ `tabWork Order` wo INNER JOIN `tabStock Entry` se
+ ON wo.name=se.work_order
+ WHERE
+ process_loss_qty > 0
+ AND wo.company = %(company)s
+ AND se.docstatus = 1
+ AND se.posting_date BETWEEN %(from_date)s AND %(to_date)s
+ {item_filter}
+ {work_order_filter}
+ GROUP BY
+ se.work_order
+ """.format(**query_args), query_args, as_dict=1, debug=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
+
+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}'"}
+ )
+ 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}'"}
+ )
+ return filter_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 8fffbcc..2e298e7 100644
--- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
+++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
-from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
from six import iteritems
+from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
+
def execute(filters=None):
if not filters:
diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js
index d16485e..695efac 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js
@@ -6,4 +6,3 @@
filters: erpnext.get_purchase_trends_filters()
}
});
-
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 8227f15..2959532 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe import _
-from erpnext.controllers.trends import get_columns,get_data
+
+from erpnext.controllers.trends import get_columns, get_data
+
def execute(filters=None):
if not filters: filters ={}
@@ -48,4 +50,4 @@
},
"type" : "bar",
"colors":["#5e64ff"]
- }
\ No newline at end of file
+ }
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 c3339fd..897a130 100644
--- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe import _
+
from erpnext.stock.stock_ledger import get_stock_ledger_entries
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
def execute(filters=None):
columns = get_columns(filters)
@@ -50,4 +52,3 @@
def get_data(filters):
return get_stock_ledger_entries(filters, '<=', order="asc") or []
-
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js
index 8495142..db463b7 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.js
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.js
@@ -22,7 +22,15 @@
"fieldname":"warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
- "options": "Warehouse"
+ "options": "Warehouse",
+ get_query: () => {
+ const company = frappe.query_report.get_filter_value("company");
+ return {
+ filters: {
+ ...company && {company},
+ }
+ };
+ }
},
{
"fieldname":"item_code",
@@ -64,4 +72,4 @@
"default": 0
}
]
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 623dc2f..b4eca0b 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -2,17 +2,22 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
+from operator import itemgetter
+
import frappe
from frappe import _
-from frappe.utils import date_diff, flt, cint
+from frappe.utils import cint, date_diff, flt
from six import iteritems
+
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
def execute(filters=None):
columns = get_columns(filters)
item_details = get_fifo_queue(filters)
to_date = filters["to_date"]
- _func = lambda x: x[1]
+ _func = itemgetter(1)
data = []
for item, item_dict in iteritems(item_details):
@@ -26,7 +31,7 @@
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)
+ 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]
@@ -58,19 +63,21 @@
return flt(age_qty / total_qty, 2) if total_qty else 0.0
-def get_range_age(filters, fifo_queue, to_date):
+def get_range_age(filters, fifo_queue, to_date, item_dict):
range1 = range2 = range3 = above_range3 = 0.0
+
for item in fifo_queue:
age = date_diff(to_date, item[1])
+ qty = flt(item[0]) if not item_dict["has_serial_no"] else 1.0
if age <= filters.range1:
- range1 += flt(item[0])
+ range1 += qty
elif age <= filters.range2:
- range2 += flt(item[0])
+ range2 += qty
elif age <= filters.range3:
- range3 += flt(item[0])
+ range3 += qty
else:
- above_range3 += flt(item[0])
+ above_range3 += qty
return range1, range2, range3, above_range3
@@ -197,9 +204,7 @@
fifo_queue.append([d.actual_qty, d.posting_date])
else:
if serial_no_list:
- for serial_no in fifo_queue:
- if serial_no[0] in serial_no_list:
- fifo_queue.remove(serial_no)
+ fifo_queue[:] = [serial_no for serial_no in fifo_queue if serial_no[0] not in serial_no_list]
else:
qty_to_pop = abs(d.actual_qty)
while qty_to_pop:
@@ -222,14 +227,16 @@
else:
item_details[key]["total_qty"] += d.actual_qty
+ item_details[key]["has_serial_no"] = d.has_serial_no
+
return item_details
def get_stock_ledger_entries(filters):
return frappe.db.sql("""select
- item.name, item.item_name, item_group, brand, description, item.stock_uom,
+ item.name, item.item_name, item_group, brand, description, item.stock_uom, item.has_serial_no,
actual_qty, posting_date, voucher_type, voucher_no, serial_no, batch_no, qty_after_transaction, warehouse
from `tabStock Ledger Entry` sle,
- (select name, item_name, description, stock_uom, brand, item_group
+ (select name, item_name, description, stock_uom, brand, item_group, has_serial_no
from `tabItem` {item_conditions}) item
where item_code = item.name and
company = %(company)s and
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js
index 6b384e2..78afe6d 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.js
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.js
@@ -37,11 +37,25 @@
default: "",
},
{
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1,
+ },
+ {
fieldname: "warehouse",
label: __("Warehouse"),
fieldtype: "Link",
- options:"Warehouse",
+ options: "Warehouse",
default: "",
+ get_query: function() {
+ const company = frappe.query_report.get_filter_value('company');
+ return {
+ filters: { 'company': company }
+ }
+ }
},
{
fieldname: "from_date",
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
index fde934b..ddc8310 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -1,14 +1,20 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
+import datetime
-from __future__ import unicode_literals
import frappe
from frappe import _, scrub
-from frappe.utils import getdate, flt
-from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details)
+from frappe.utils import get_first_day as get_first_day_of_month
+from frappe.utils import get_first_day_of_week, get_quarter_start, getdate
+
from erpnext.accounts.utils import get_fiscal_year
+from erpnext.stock.report.stock_balance.stock_balance import (
+ get_item_details,
+ get_items,
+ get_stock_ledger_entries,
+)
from erpnext.stock.utils import is_reposting_item_valuation_in_progress
-from six import iteritems
+
def execute(filters=None):
is_reposting_item_valuation_in_progress()
@@ -71,7 +77,8 @@
def get_period_date_ranges(filters):
from dateutil.relativedelta import relativedelta
- from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
+ from_date = round_down_to_nearest_frequency(filters.from_date, filters.range)
+ to_date = getdate(filters.to_date)
increment = {
"Monthly": 1,
@@ -97,6 +104,31 @@
return periodic_daterange
+
+def round_down_to_nearest_frequency(date: str, frequency: str) -> datetime.datetime:
+ """Rounds down the date to nearest frequency unit.
+ example:
+
+ >>> round_down_to_nearest_frequency("2021-02-21", "Monthly")
+ datetime.datetime(2021, 2, 1)
+
+ >>> round_down_to_nearest_frequency("2021-08-21", "Yearly")
+ datetime.datetime(2021, 1, 1)
+ """
+
+ def _get_first_day_of_fiscal_year(date):
+ fiscal_year = get_fiscal_year(date)
+ return fiscal_year and fiscal_year[1] or date
+
+ round_down_function = {
+ "Monthly": get_first_day_of_month,
+ "Quarterly": get_quarter_start,
+ "Weekly": get_first_day_of_week,
+ "Yearly": _get_first_day_of_fiscal_year,
+ }.get(frequency, getdate)
+ return round_down_function(date)
+
+
def get_period(posting_date, filters):
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
@@ -177,7 +209,7 @@
periodic_data = get_periodic_data(sle, filters)
ranges = get_period_date_ranges(filters)
- for dummy, item_data in iteritems(item_details):
+ for dummy, item_data in item_details.items():
row = {
"name": item_data.name,
"item_name": item_data.item_name,
@@ -208,7 +240,3 @@
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
new file mode 100644
index 0000000..21e1205
--- /dev/null
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -0,0 +1,35 @@
+import datetime
+import unittest
+
+from frappe import _dict
+
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
+
+
+class TestStockAnalyticsReport(unittest.TestCase):
+ def test_get_period_date_ranges(self):
+
+ filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
+
+ ranges = get_period_date_ranges(filters)
+
+ expected_ranges = [
+ [datetime.date(2020, 12, 1), datetime.date(2020, 12, 31)],
+ [datetime.date(2021, 1, 1), datetime.date(2021, 1, 31)],
+ [datetime.date(2021, 2, 1), datetime.date(2021, 2, 6)],
+ ]
+
+ self.assertEqual(ranges, expected_ranges)
+
+ def test_get_period_date_ranges_yearly(self):
+
+ filters = _dict(range="Yearly", from_date="2021-01-28", to_date="2021-02-06")
+
+ ranges = get_period_date_ranges(filters)
+ first_date = get_fiscal_year("2021-01-28")[1]
+ expected_ranges = [
+ [first_date, datetime.date(2021, 2, 6)],
+ ]
+
+ self.assertEqual(ranges, expected_ranges)
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 bfc4471..f64774a 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
@@ -2,12 +2,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+
+import frappe
from frappe import _
-from erpnext.accounts.utils import get_stock_accounts
-from erpnext.accounts.utils import get_currency_precision
+
+import erpnext
+from erpnext.accounts.utils import get_currency_precision, get_stock_accounts
from erpnext.stock.doctype.warehouse.warehouse import get_warehouses_based_on_account
+
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.")
@@ -128,4 +131,4 @@
"fieldtype": "Currency",
"width": "120"
}
- ]
\ No newline at end of file
+ ]
diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js
index 7d22823..ce6ffa0 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.js
+++ b/erpnext/stock/report/stock_balance/stock_balance.js
@@ -53,13 +53,14 @@
"width": "80",
"options": "Warehouse",
get_query: () => {
- var warehouse_type = frappe.query_report.get_filter_value('warehouse_type');
- if(warehouse_type){
- return {
- filters: {
- 'warehouse_type': warehouse_type
- }
- };
+ let warehouse_type = frappe.query_report.get_filter_value("warehouse_type");
+ let company = frappe.query_report.get_filter_value("company");
+
+ return {
+ filters: {
+ ...warehouse_type && {warehouse_type},
+ ...company && {company}
+ }
}
}
},
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index fc3d719..fc5d5c1 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -2,16 +2,20 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe import _
-from frappe.utils import flt, cint, getdate, now, date_diff
-from erpnext.stock.utils import add_additional_uom_columns
-from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
-from erpnext.stock.utils import is_reposting_item_valuation_in_progress
-from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age
+from operator import itemgetter
+
+import frappe
+from frappe import _
+from frappe.utils import cint, date_diff, flt, getdate
from six import iteritems
+import erpnext
+from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue
+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 = {}
@@ -44,7 +48,7 @@
data = []
conversion_factors = {}
- _func = lambda x: x[1]
+ _func = itemgetter(1)
for (company, item, warehouse) in sorted(iwb_map):
if item_map.get(item):
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 8909f21..1ea58fe 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -4,10 +4,15 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cint, flt
-from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress
from frappe import _
+from frappe.utils import cint, flt
+
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.utils import (
+ is_reposting_item_valuation_in_progress,
+ update_included_uom_in_report,
+)
+
def execute(filters=None):
is_reposting_item_valuation_in_progress()
@@ -23,6 +28,7 @@
conversion_factors = []
if opening_row:
data.append(opening_row)
+ conversion_factors.append(0)
actual_qty = stock_value = 0
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 808d279..16f7c30 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -2,11 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt, today
-from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress
+
from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_pos_reserved_qty
+from erpnext.stock.utils import (
+ is_reposting_item_valuation_in_progress,
+ update_included_uom_in_report,
+)
+
def execute(filters=None):
is_reposting_item_valuation_in_progress()
@@ -32,7 +38,7 @@
if filters.brand and filters.brand != item.brand:
continue
-
+
elif filters.item_group and filters.item_group != item.item_group:
continue
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 78e95df..5580636 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
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
validate_warehouse(filters)
columns = get_columns()
@@ -58,14 +60,14 @@
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",
+ 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'],
+ 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.
@@ -84,4 +86,4 @@
data.append(row)
- return data
\ No newline at end of file
+ return data
diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js
index cdc9895..5b00647 100644
--- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js
+++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js
@@ -25,4 +25,4 @@
"default": frappe.datetime.month_end()
},
]
-}
\ No newline at end of file
+}
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 4108a57..f15557b 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
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
from six import iteritems
+
def execute(filters=None):
columns = get_columns(filters)
consumed_details = get_consumed_details(filters)
diff --git a/erpnext/stock/report/test_reports.py b/erpnext/stock/report/test_reports.py
new file mode 100644
index 0000000..d7fb5b2
--- /dev/null
+++ b/erpnext/stock/report/test_reports.py
@@ -0,0 +1,63 @@
+import unittest
+from typing import List, Tuple
+
+from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
+
+DEFAULT_FILTERS = {
+ "company": "_Test Company",
+ "from_date": "2010-01-01",
+ "to_date": "2030-01-01",
+}
+
+
+REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [
+ ("Stock Ledger", {"_optional": True}),
+ ("Stock Balance", {"_optional": True}),
+ ("Stock Projected Qty", {"_optional": True}),
+ ("Batch-Wise Balance History", {}),
+ ("Itemwise Recommended Reorder Level", {"item_group": "All Item Groups"}),
+ ("COGS By Item Group", {}),
+ ("Stock Qty vs Serial No Count", {"warehouse": "_Test Warehouse - _TC"}),
+ (
+ "Stock and Account Value Comparison",
+ {
+ "company": "_Test Company with perpetual inventory",
+ "account": "Stock In Hand - TCP1",
+ "as_on_date": "2021-01-01",
+ },
+ ),
+ ("Product Bundle Balance", {"date": "2022-01-01", "_optional": True}),
+ (
+ "Stock Analytics",
+ {
+ "from_date": "2021-01-01",
+ "to_date": "2021-12-31",
+ "value_quantity": "Quantity",
+ "_optional": True,
+ },
+ ),
+ ("Warehouse wise Item Balance Age and Value", {"_optional": True}),
+ ("Item Variant Details", {"item": "_Test Variant Item",}),
+ ("Total Stock Summary", {"group_by": "warehouse",}),
+ ("Batch Item Expiry Status", {}),
+ ("Stock Ageing", {"range1": 30, "range2": 60, "range3": 90, "_optional": True}),
+]
+
+OPTIONAL_FILTERS = {
+ "warehouse": "_Test Warehouse - _TC",
+ "item": "_Test Item",
+ "item_group": "_Test Item Group",
+}
+
+
+class TestReports(unittest.TestCase):
+ def test_execute_all_stock_reports(self):
+ """Test that all script report in stock modules are executable with supported filters"""
+ for report, filter in REPORT_FILTER_TEST_CASES:
+ execute_script_report(
+ report_name=report,
+ module="Stock",
+ filters=filter,
+ default_filters=DEFAULT_FILTERS,
+ optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None,
+ )
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.js b/erpnext/stock/report/total_stock_summary/total_stock_summary.js
index 2646428..90648f1 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js
@@ -38,4 +38,4 @@
"reqd": 1
},
]
-}
\ No newline at end of file
+}
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 59c253c..779b5aa 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -2,9 +2,11 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
+
def execute(filters=None):
if not filters: filters = {}
validate_filters(filters)
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 04f7d34..6cb3751 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
@@ -5,15 +5,22 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import flt, cint, getdate
-from erpnext.stock.report.stock_balance.stock_balance import (get_item_details,
- get_item_reorder_details, get_item_warehouse_map, get_items, get_stock_ledger_entries)
-from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age
-from erpnext.stock.utils import is_reposting_item_valuation_in_progress
+from frappe.utils import flt
from six import iteritems
+from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue
+from erpnext.stock.report.stock_balance.stock_balance import (
+ get_item_details,
+ get_item_warehouse_map,
+ get_items,
+ get_stock_ledger_entries,
+)
+from erpnext.stock.utils import is_reposting_item_valuation_in_progress
+
+
def execute(filters=None):
is_reposting_item_valuation_in_progress()
if not filters: filters = {}
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index 8917bfe..1cb0f0d 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -2,11 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import print_function, unicode_literals
+
import frappe
-from frappe.utils import flt, cstr, nowdate, nowtime
-from erpnext.stock.utils import update_bin
-from erpnext.stock.stock_ledger import update_entries_after
+from frappe.utils import cstr, flt, nowdate, nowtime
+
from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
+from erpnext.stock.utils import update_bin
+
def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False):
"""
@@ -29,7 +31,7 @@
try:
repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin, allow_negative_stock)
frappe.db.commit()
- except:
+ except Exception:
frappe.db.rollback()
if allow_negative_stock:
@@ -247,5 +249,5 @@
sr.via_stock_ledger = True
sr.save()
- except:
+ except Exception:
pass
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index eddd048..1b5b792 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -2,17 +2,22 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-import erpnext
import copy
-from frappe import _
-from frappe.utils import cint, flt, cstr, now, get_link_to_form, getdate
-from frappe.model.meta import get_field_precision
-from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
-from erpnext.stock.utils import get_bin
import json
+
+import frappe
+from frappe import _
+from frappe.model.meta import get_field_precision
+from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, now
from six import iteritems
+import erpnext
+from erpnext.stock.utils import (
+ get_bin,
+ get_incoming_outgoing_rate_for_cancel,
+ get_valuation_method,
+)
+
# future reposting
class NegativeStockError(frappe.ValidationError): pass
@@ -279,13 +284,15 @@
}
"""
+ self.data.setdefault(args.warehouse, frappe._dict())
+ warehouse_dict = self.data[args.warehouse]
previous_sle = get_previous_sle_of_current_voucher(args)
+ warehouse_dict.previous_sle = previous_sle
- self.data[args.warehouse] = frappe._dict({
- "previous_sle": previous_sle,
- "qty_after_transaction": flt(previous_sle.qty_after_transaction),
- "valuation_rate": flt(previous_sle.valuation_rate),
- "stock_value": flt(previous_sle.stock_value),
+ 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
@@ -332,6 +339,7 @@
where
item_code = %(item_code)s
and warehouse = %(warehouse)s
+ and is_cancelled = 0
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
@@ -399,7 +407,8 @@
return
# Get dynamic incoming/outgoing rate
- self.get_dynamic_incoming_outgoing_rate(sle)
+ if not self.args.get("sle_id"):
+ self.get_dynamic_incoming_outgoing_rate(sle)
if sle.serial_no:
self.get_serialized_values(sle)
@@ -439,7 +448,8 @@
sle.doctype="Stock Ledger Entry"
frappe.get_doc(sle).db_update()
- self.update_outgoing_rate_on_transaction(sle)
+ if not self.args.get("sle_id"):
+ self.update_outgoing_rate_on_transaction(sle)
def validate_negative_stock(self, sle):
"""
@@ -475,7 +485,9 @@
# Sales and Purchase Return
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
+ 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)
else:
@@ -671,11 +683,15 @@
if self.wh_data.stock_queue[-1][1]==incoming_rate:
self.wh_data.stock_queue[-1][0] += actual_qty
else:
+ # Item has a positive balance qty, add new entry
if self.wh_data.stock_queue[-1][0] > 0:
self.wh_data.stock_queue.append([actual_qty, incoming_rate])
- else:
+ else: # negative balance qty
qty = self.wh_data.stock_queue[-1][0] + actual_qty
- self.wh_data.stock_queue[-1] = [qty, incoming_rate]
+ if qty > 0: # new balance qty is positive
+ self.wh_data.stock_queue[-1] = [qty, incoming_rate]
+ else: # new balance qty is still negative, maintain same rate
+ self.wh_data.stock_queue[-1][0] = qty
else:
qty_to_pop = abs(actual_qty)
while qty_to_pop:
@@ -954,7 +970,7 @@
return valuation_rate
-def update_qty_in_future_sle(args, allow_negative_stock=None):
+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
@@ -1043,8 +1059,8 @@
)
)"""
-def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
- allow_negative_stock = allow_negative_stock \
+def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
+ allow_negative_stock = cint(allow_negative_stock) \
or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
if (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation") and not allow_negative_stock:
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 9f6d0a8..aeb06e9 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -2,13 +2,17 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
-from frappe import _
-import json
-from frappe.utils import flt, cstr, nowdate, nowtime, get_link_to_form
+import json
+
+import frappe
+from frappe import _
+from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
from six import string_types
+import erpnext
+
+
class InvalidWarehouseCompany(frappe.ValidationError): pass
def get_stock_value_from_bin(warehouse=None, item_code=None):
diff --git a/erpnext/support/__init__.py b/erpnext/support/__init__.py
index cc26c14..bd1b3f8 100644
--- a/erpnext/support/__init__.py
+++ b/erpnext/support/__init__.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
install_docs = [
{'doctype':'Role', 'role_name':'Support Team', 'name':'Support Team'},
{'doctype':'Role', 'role_name':'Maintenance User', 'name':'Maintenance User'},
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 9ac1efa..d4daacd 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -145,4 +145,4 @@
// frm.timeline.wrapper.data("help-article-event-attached", true);
// }
},
-});
\ No newline at end of file
+});
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 24dadd5..0fe1068 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -2,16 +2,19 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import now_datetime, time_diff_in_seconds, get_datetime, date_diff
-from frappe.core.utils import get_parent_doc
from datetime import timedelta
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils.user import is_website_user
+
+import frappe
+from frappe import _
+from frappe.core.utils import get_parent_doc
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.utils import date_diff, get_datetime, now_datetime, time_diff_in_seconds
+from frappe.utils.user import is_website_user
+
class Issue(Document):
def get_feed(self):
@@ -116,7 +119,7 @@
}).insert(ignore_permissions=True)
return replicated_issue.name
-
+
def reset_issue_metrics(self):
self.db_set("resolution_time", None)
self.db_set("user_resolution_time", None)
@@ -225,13 +228,13 @@
def set_first_response_time(communication, method):
if communication.get('reference_doctype') == "Issue":
issue = get_parent_doc(communication)
- if is_first_response(issue):
+ if is_first_response(issue) and issue.service_level_agreement:
first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
issue.db_set("first_response_time", first_response_time)
def is_first_response(issue):
responses = frappe.get_all('Communication', filters = {'reference_name': issue.name, 'sent_or_received': 'Sent'})
- if len(responses) == 1:
+ if len(responses) == 1:
return True
return False
@@ -260,7 +263,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
-
+
else:
return 1.0
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 84f8c39..6f0b8a6 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -2,74 +2,82 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
-from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
-from frappe.core.doctype.user_permission.test_user_permission import create_user
-from frappe.utils import get_datetime, flt
import datetime
-from datetime import timedelta
+import unittest
+
+import frappe
+from frappe.core.doctype.user_permission.test_user_permission import create_user
+from frappe.utils import flt, get_datetime
+
+from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import (
+ create_service_level_agreements_for_issues,
+)
+
class TestSetUp(unittest.TestCase):
def setUp(self):
frappe.db.sql("delete from `tabService Level Agreement`")
+ frappe.db.sql("delete from `tabService Level Priority`")
+ frappe.db.sql("delete from `tabSLA Fulfilled On Status`")
+ frappe.db.sql("delete from `tabPause SLA On Status`")
+ frappe.db.sql("delete from `tabService Day`")
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 = datetime.datetime(2019, 3, 4, 12, 0)
+ creation = get_datetime("2019-03-04 12:00")
# make issue with customer specific SLA
customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory")
issue = make_issue(creation, "_Test Customer", 1)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
+ 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 customer_group specific SLA
customer = create_customer("__Test Customer", "_Test SLA Customer Group", "__Test SLA Territory")
issue = make_issue(creation, "__Test Customer", 2)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
+ 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")
issue = make_issue(creation, "___Test Customer", 3)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
+ 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 default SLA
issue = make_issue(creation=creation, index=4)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 16, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 18, 0))
+ self.assertEqual(issue.response_by, get_datetime("2019-03-04 16:00"))
+ self.assertEqual(issue.resolution_by, get_datetime("2019-03-04 18:00"))
# make issue with default SLA before working hours
- creation = datetime.datetime(2019, 3, 4, 7, 0)
+ creation = get_datetime("2019-03-04 7:00")
issue = make_issue(creation=creation, index=5)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 16, 0))
+ self.assertEqual(issue.response_by, get_datetime("2019-03-04 14:00"))
+ self.assertEqual(issue.resolution_by, get_datetime("2019-03-04 16:00"))
# make issue with default SLA after working hours
- creation = datetime.datetime(2019, 3, 4, 20, 0)
+ creation = get_datetime("2019-03-04 20:00")
issue = make_issue(creation, index=6)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 6, 14, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 6, 16, 0))
+ self.assertEqual(issue.response_by, get_datetime("2019-03-06 14:00"))
+ self.assertEqual(issue.resolution_by, get_datetime("2019-03-06 16:00"))
# make issue with default SLA next day
- creation = datetime.datetime(2019, 3, 4, 14, 0)
+ creation = get_datetime("2019-03-04 14:00")
issue = make_issue(creation=creation, index=7)
- self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 18, 0))
- self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 6, 12, 0))
+ self.assertEqual(issue.response_by, get_datetime("2019-03-04 18:00"))
+ self.assertEqual(issue.resolution_by, get_datetime("2019-03-06 12:00"))
- frappe.flags.current_time = datetime.datetime(2019, 3, 4, 15, 0)
+ frappe.flags.current_time = get_datetime("2019-03-04 15:00")
issue.reload()
issue.status = 'Closed'
issue.save()
@@ -77,21 +85,21 @@
self.assertEqual(issue.agreement_status, 'Fulfilled')
def test_issue_metrics(self):
- creation = datetime.datetime(2020, 3, 4, 4, 0)
+ creation = get_datetime("2020-03-04 4:00")
issue = make_issue(creation, index=1)
create_communication(issue.name, "test@example.com", "Received", creation)
- creation = datetime.datetime(2020, 3, 4, 4, 15)
+ creation = get_datetime("2020-03-04 4:15")
create_communication(issue.name, "test@admin.com", "Sent", creation)
- creation = datetime.datetime(2020, 3, 4, 5, 0)
+ creation = get_datetime("2020-03-04 5:00")
create_communication(issue.name, "test@example.com", "Received", creation)
- creation = datetime.datetime(2020, 3, 4, 5, 5)
+ creation = get_datetime("2020-03-04 5:05")
create_communication(issue.name, "test@admin.com", "Sent", creation)
- frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5)
+ frappe.flags.current_time = get_datetime("2020-03-04 5:05")
issue.reload()
issue.status = 'Closed'
issue.save()
@@ -101,33 +109,33 @@
self.assertEqual(issue.user_resolution_time, 1200)
def test_hold_time_on_replied(self):
- creation = datetime.datetime(2020, 3, 4, 4, 0)
+ creation = get_datetime("2020-03-04 4:00")
issue = make_issue(creation, index=1)
create_communication(issue.name, "test@example.com", "Received", creation)
- creation = datetime.datetime(2020, 3, 4, 4, 15)
+ creation = get_datetime("2020-03-04 4:15")
create_communication(issue.name, "test@admin.com", "Sent", creation)
- frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15)
+ frappe.flags.current_time = get_datetime("2020-03-04 4:15")
issue.reload()
issue.status = 'Replied'
issue.save()
self.assertEqual(issue.on_hold_since, frappe.flags.current_time)
- creation = datetime.datetime(2020, 3, 4, 5, 0)
- frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 0)
+ creation = get_datetime("2020-03-04 5:00")
+ frappe.flags.current_time = get_datetime("2020-03-04 5:00")
create_communication(issue.name, "test@example.com", "Received", creation)
issue.reload()
self.assertEqual(flt(issue.total_hold_time, 2), 2700)
- self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45))
+ self.assertEqual(issue.resolution_by, get_datetime("2020-03-04 16:45"))
- creation = datetime.datetime(2020, 3, 4, 5, 5)
+ creation = get_datetime("2020-03-04 5:05")
create_communication(issue.name, "test@admin.com", "Sent", creation)
- frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5)
+ frappe.flags.current_time = get_datetime("2020-03-04 5:05")
issue.reload()
issue.status = 'Closed'
issue.save()
@@ -178,7 +186,7 @@
# 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"))
self.assertEqual(issue.first_response_time, 28800.0)
@@ -200,7 +208,7 @@
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.
- """
+ """
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)
@@ -228,7 +236,7 @@
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.
- """
+ """
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)
@@ -344,7 +352,7 @@
"""
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")
@@ -418,4 +426,4 @@
"creation": creation,
"reference_name": reference_name
})
- communication.save()
\ No newline at end of file
+ communication.save()
diff --git a/erpnext/support/doctype/issue_priority/issue_priority.py b/erpnext/support/doctype/issue_priority/issue_priority.py
index 7c8925e..1a7daf6 100644
--- a/erpnext/support/doctype/issue_priority/issue_priority.py
+++ b/erpnext/support/doctype/issue_priority/issue_priority.py
@@ -3,9 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe import _
+
from frappe.model.document import Document
+
class IssuePriority(Document):
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/support/doctype/issue_priority/test_issue_priority.py b/erpnext/support/doctype/issue_priority/test_issue_priority.py
index a7b55f8..fc9ae68 100644
--- a/erpnext/support/doctype/issue_priority/test_issue_priority.py
+++ b/erpnext/support/doctype/issue_priority/test_issue_priority.py
@@ -3,9 +3,11 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
+
class TestIssuePriority(unittest.TestCase):
def test_priorities(self):
@@ -25,4 +27,4 @@
frappe.get_doc({
"doctype": "Issue Priority",
"name": name
- }).insert(ignore_permissions=True)
\ No newline at end of file
+ }).insert(ignore_permissions=True)
diff --git a/erpnext/support/doctype/issue_type/issue_type.py b/erpnext/support/doctype/issue_type/issue_type.py
index f95d09c..089ae28 100644
--- a/erpnext/support/doctype/issue_type/issue_type.py
+++ b/erpnext/support/doctype/issue_type/issue_type.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class IssueType(Document):
pass
diff --git a/erpnext/support/doctype/issue_type/test_issue_type.py b/erpnext/support/doctype/issue_type/test_issue_type.py
index 4e3b66a..06a2de2 100644
--- a/erpnext/support/doctype/issue_type/test_issue_type.py
+++ b/erpnext/support/doctype/issue_type/test_issue_type.py
@@ -3,8 +3,8 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+
class TestIssueType(unittest.TestCase):
pass
diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py
index a3b547e..37c1f2b 100644
--- a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py
+++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class PauseSLAOnStatus(Document):
pass
diff --git a/erpnext/support/doctype/service_day/service_day.py b/erpnext/support/doctype/service_day/service_day.py
index 3805b5a..da96cad 100644
--- a/erpnext/support/doctype/service_day/service_day.py
+++ b/erpnext/support/doctype/service_day/service_day.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ServiceDay(Document):
pass
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
index ef14b29..5f470aa 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -18,6 +18,10 @@
"entity_type",
"column_break_10",
"entity",
+ "filters_section",
+ "condition",
+ "column_break_15",
+ "condition_description",
"agreement_details_section",
"start_date",
"column_break_7",
@@ -176,13 +180,34 @@
"fieldname": "apply_sla_for_resolution",
"fieldtype": "Check",
"label": "Apply SLA for Resolution Time"
+ },
+ {
+ "fieldname": "filters_section",
+ "fieldtype": "Section Break",
+ "label": "Assignment Condition"
+ },
+ {
+ "fieldname": "column_break_15",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "condition",
+ "fieldtype": "Code",
+ "label": "Condition",
+ "options": "Python"
+ },
+ {
+ "fieldname": "condition_description",
+ "fieldtype": "HTML",
+ "options": "<p><strong>Condition Examples:</strong></p>\n<pre>doc.status==\"Open\"<br>doc.due_date==nowdate()<br>doc.total > 40000\n</pre>"
}
],
"links": [],
- "modified": "2021-07-08 12:28:46.283334",
+ "modified": "2021-10-02 11:32:55.556024",
"modified_by": "Administrator",
"module": "Support",
"name": "Service Level Agreement",
+ "naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
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 cfa264f..6bdd8f2 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -3,21 +3,39 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
+from datetime import datetime
+
import frappe
-from frappe.model.document import Document
from frappe import _
from frappe.core.utils import get_parent_doc
-from frappe.utils import time_diff_in_seconds, getdate, get_weekdays, add_to_date, get_time, get_datetime, \
- get_time_zone, to_timedelta, get_datetime_str, get_link_to_form, cint
-from datetime import datetime
+from frappe.model.document import Document
+from frappe.utils import (
+ add_to_date,
+ cint,
+ get_datetime,
+ get_datetime_str,
+ get_link_to_form,
+ get_time,
+ get_time_zone,
+ get_weekdays,
+ getdate,
+ nowdate,
+ time_diff_in_seconds,
+ to_timedelta,
+)
+from frappe.utils.safe_exec import get_safe_globals
+
from erpnext.support.doctype.issue.issue import get_holidays
+
class ServiceLevelAgreement(Document):
def validate(self):
self.validate_doc()
self.validate_status_field()
self.check_priorities()
self.check_support_and_resolution()
+ self.validate_condition()
def check_priorities(self):
priorities = []
@@ -96,6 +114,14 @@
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)
+ if self.condition:
+ try:
+ frappe.safe_eval(self.condition, None, get_context(temp_doc))
+ except Exception:
+ frappe.throw(_("The Condition '{0}' is invalid").format(self.condition))
+
def get_service_level_agreement_priority(self, priority):
priority = frappe.get_doc("Service Level Priority", {"priority": priority, "parent": self.name})
@@ -204,35 +230,51 @@
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(doctype, priority, customer=None, service_level_agreement=None):
- if doctype == "Issue" and not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
+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", "=", doctype],
+ ["Service Level Agreement", "document_type", "=", doc.get('doctype')],
["Service Level Agreement", "enabled", "=", 1]
]
- if priority:
- filters.append(["Service Level Priority", "priority", "=", priority])
+
+ if doc.get('priority'):
+ filters.append(["Service Level Priority", "priority", "=", doc.get('priority')])
or_filters = []
- if service_level_agreement:
+ if doc.get('service_level_agreement'):
or_filters = [
- ["Service Level Agreement", "name", "=", service_level_agreement],
+ ["Service Level Agreement", "name", "=", doc.get('service_level_agreement')],
]
- if customer:
- or_filters.append(
- ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]]
- )
- or_filters.append(["Service Level Agreement", "default_service_level_agreement", "=", 1])
+ customer = doc.get('customer')
+ or_filters.append(
+ ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]]
+ )
- agreement = frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters,
- fields=["name", "default_priority", "apply_sla_for_resolution"])
+ 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"])
- return agreement[0] if agreement else None
+ 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"])
+ # 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')
+ if not condition or (condition and frappe.safe_eval(condition, None, get_context(doc))):
+ filtered_agreements.append(agreement)
+
+ # if any default sla
+ filtered_agreements += default_sla
+
+ 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"))}
def get_customer_group(customer):
return frappe.db.get_value("Customer", customer, "customer_group") if customer else None
@@ -297,12 +339,11 @@
def apply(doc, method=None):
# Applies SLA to document on validate
- if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard or \
+ if frappe.flags.in_patch or frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_setup_wizard or \
doc.doctype not in get_documents_with_active_service_level_agreement():
return
- service_level_agreement = get_active_service_level_agreement_for(doctype=doc.get("doctype"), priority=doc.get("priority"),
- customer=doc.get("customer"), service_level_agreement=doc.get("service_level_agreement"))
+ service_level_agreement = get_active_service_level_agreement_for(doc)
if not service_level_agreement:
return
@@ -819,7 +860,7 @@
def convert_utc_to_user_timezone(utc_timestamp, user):
- from pytz import timezone, UnknownTimeZoneError
+ from pytz import UnknownTimeZoneError, timezone
user_tz = get_tz(user)
utcnow = timezone('UTC').localize(utc_timestamp)
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py
index f2bd681..22e2c37 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py
@@ -1,5 +1,6 @@
from frappe import _
+
def get_data():
return {
'fieldname': 'service_level_agreement',
@@ -9,4 +10,4 @@
'items': ['Issue']
}
]
- }
\ No newline at end of file
+ }
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 7bc97d6..3a92277 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
@@ -3,12 +3,17 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
-import unittest
import datetime
+import unittest
+
+import frappe
from frappe.utils import flt
+
from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities
-from erpnext.support.doctype.service_level_agreement.service_level_agreement import get_service_level_agreement_fields
+from erpnext.support.doctype.service_level_agreement.service_level_agreement import (
+ get_service_level_agreement_fields,
+)
+
class TestServiceLevelAgreement(unittest.TestCase):
def setUp(self):
@@ -253,6 +258,30 @@
lead.reload()
self.assertEqual(lead.response_by_variance, 1800.0)
+ def test_service_level_agreement_filters(self):
+ doctype = "Lead"
+ lead_sla = create_service_level_agreement(
+ default_service_level_agreement=0,
+ doctype=doctype,
+ holiday_list="__Test Holiday List",
+ entity_type=None, entity=None,
+ condition='doc.source == "Test Source"',
+ response_time=14400,
+ sla_fulfilled_on=[{"status": "Replied"}],
+ 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')
+ self.assertFalse(applied_sla)
+
+ 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')
+ self.assertEqual(applied_sla, lead_sla.name)
+
def tearDown(self):
for d in frappe.get_all("Service Level Agreement"):
frappe.delete_doc("Service Level Agreement", d.name, force=1)
@@ -268,7 +297,7 @@
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", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1):
+ entity, resolution_time=0, doctype="Issue", condition="", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1):
make_holiday_list()
make_priorities()
@@ -287,6 +316,7 @@
"document_type": doctype,
"service_level": "__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,
@@ -488,5 +518,6 @@
"lead_name": "_Test Lead {0}".format(index),
"status": "Open",
"creation": creation,
- "service_level_agreement_creation": creation
- }).insert(ignore_permissions=True)
\ No newline at end of file
+ "service_level_agreement_creation": creation,
+ "priority": "Medium"
+ }).insert(ignore_permissions=True)
diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.py b/erpnext/support/doctype/service_level_priority/service_level_priority.py
index 0c0fe4a..3b2d124 100644
--- a/erpnext/support/doctype/service_level_priority/service_level_priority.py
+++ b/erpnext/support/doctype/service_level_priority/service_level_priority.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class ServiceLevelPriority(Document):
pass
diff --git a/erpnext/support/doctype/sla_fulfilled_on_status/sla_fulfilled_on_status.py b/erpnext/support/doctype/sla_fulfilled_on_status/sla_fulfilled_on_status.py
index b0b5ffc..dbffcb8 100644
--- a/erpnext/support/doctype/sla_fulfilled_on_status/sla_fulfilled_on_status.py
+++ b/erpnext/support/doctype/sla_fulfilled_on_status/sla_fulfilled_on_status.py
@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
+
class SLAFulfilledOnStatus(Document):
pass
diff --git a/erpnext/support/doctype/support_search_source/support_search_source.py b/erpnext/support/doctype/support_search_source/support_search_source.py
index 93e503a..50f4714 100644
--- a/erpnext/support/doctype/support_search_source/support_search_source.py
+++ b/erpnext/support/doctype/support_search_source/support_search_source.py
@@ -3,7 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
from frappe.model.document import Document
+
class SupportSearchSource(Document):
pass
diff --git a/erpnext/support/doctype/support_settings/support_settings.py b/erpnext/support/doctype/support_settings/support_settings.py
index bb3c53a..73aad9d 100644
--- a/erpnext/support/doctype/support_settings/support_settings.py
+++ b/erpnext/support/doctype/support_settings/support_settings.py
@@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
from frappe.model.document import Document
+
class SupportSettings(Document):
pass
diff --git a/erpnext/support/doctype/support_settings/test_support_settings.js b/erpnext/support/doctype/support_settings/test_support_settings.js
deleted file mode 100644
index 0787306..0000000
--- a/erpnext/support/doctype/support_settings/test_support_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Support Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Support Settings
- () => frappe.tests.make('Support Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/support/doctype/support_settings/test_support_settings.py b/erpnext/support/doctype/support_settings/test_support_settings.py
index 9c47f96..e9ec070 100644
--- a/erpnext/support/doctype/support_settings/test_support_settings.py
+++ b/erpnext/support/doctype/support_settings/test_support_settings.py
@@ -5,5 +5,6 @@
import unittest
+
class TestSupportSettings(unittest.TestCase):
pass
diff --git a/erpnext/support/doctype/warranty_claim/test_warranty_claim.py b/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
index 909675a..dac8f03 100644
--- a/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/test_warranty_claim.py
@@ -2,9 +2,10 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
+import frappe
+
test_records = frappe.get_test_records('Warranty Claim')
class TestWarrantyClaim(unittest.TestCase):
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.py b/erpnext/support/doctype/warranty_claim/warranty_claim.py
index a20e7a8..5fb1124 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.py
@@ -3,14 +3,14 @@
from __future__ import unicode_literals
+
import frappe
-from frappe import session, _
-from frappe.utils import today, now_datetime
-
-
+from frappe import _, session
+from frappe.utils import now_datetime
from erpnext.utilities.transaction_base import TransactionBase
+
class WarrantyClaim(TransactionBase):
def get_feed(self):
return _("{0}: From {1}").format(self.status, self.customer_name)
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 922da2b..cb7a8a6 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
@@ -2,8 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+
def execute(filters=None):
columns = [
{
@@ -32,4 +34,4 @@
ORDER BY creation_date desc
''', (filters.from_date, filters.to_date))
- return columns, data
\ No newline at end of file
+ return columns, data
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py
index 3fdb10d..ac8bce1 100644
--- a/erpnext/support/report/issue_analytics/issue_analytics.py
+++ b/erpnext/support/report/issue_analytics/issue_analytics.py
@@ -2,13 +2,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from six import iteritems
+
+import frappe
from frappe import _, scrub
-from frappe.utils import getdate, flt, add_to_date, add_days
+from frappe.utils import add_days, add_to_date, flt, getdate
+from six import iteritems
+
from erpnext.accounts.utils import get_fiscal_year
+
def execute(filters=None):
return IssueAnalytics(filters).run()
@@ -103,7 +107,7 @@
return period
def get_period_date_ranges(self):
- from dateutil.relativedelta import relativedelta, MO
+ from dateutil.relativedelta import MO, relativedelta
from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
increment = {
@@ -218,4 +222,4 @@
'datasets': []
},
'type': 'line'
- }
\ No newline at end of file
+ }
diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py
index 7748319..1de03ab 100644
--- a/erpnext/support/report/issue_analytics/test_issue_analytics.py
+++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py
@@ -1,11 +1,16 @@
from __future__ import unicode_literals
+
import unittest
+
import frappe
-from frappe.utils import getdate, add_months
-from erpnext.support.report.issue_analytics.issue_analytics import execute
-from erpnext.support.doctype.issue.test_issue import make_issue, create_customer
-from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
from frappe.desk.form.assign_to import add as add_assignment
+from frappe.utils import add_months, getdate
+
+from erpnext.support.doctype.issue.test_issue import create_customer, make_issue
+from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import (
+ create_service_level_agreements_for_issues,
+)
+from erpnext.support.report.issue_analytics.issue_analytics import execute
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
@@ -22,7 +27,7 @@
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)
-
+
def test_issue_analytics(self):
create_service_level_agreements_for_issues()
create_issue_types()
@@ -211,4 +216,4 @@
"assign_to": ["test@example.com", "test1@example.com"],
"doctype": "Issue",
"name": issue.name
- })
\ No newline at end of file
+ })
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
index bba25b8..0481996 100644
--- a/erpnext/support/report/issue_summary/issue_summary.py
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -2,11 +2,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import json
-from six import iteritems
+
+import frappe
from frappe import _, scrub
from frappe.utils import flt
+from six import iteritems
+
def execute(filters=None):
return IssueSummary(filters).run()
@@ -362,4 +365,3 @@
'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 08802b4..c0bec3c 100644
--- a/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
+++ b/erpnext/support/report/support_hour_distribution/support_hour_distribution.py
@@ -2,9 +2,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.utils import add_to_date, getdate, get_datetime
+from frappe.utils import add_to_date, get_datetime, getdate
from six import iteritems
time_slots = {
diff --git a/erpnext/support/web_form/issues/issues.js b/erpnext/support/web_form/issues/issues.js
index 699703c..ffc5e98 100644
--- a/erpnext/support/web_form/issues/issues.js
+++ b/erpnext/support/web_form/issues/issues.js
@@ -1,3 +1,3 @@
frappe.ready(function() {
// bind events here
-})
\ No newline at end of file
+})
diff --git a/erpnext/support/web_form/issues/issues.py b/erpnext/support/web_form/issues/issues.py
index 2334f8b..f57de91 100644
--- a/erpnext/support/web_form/issues/issues.py
+++ b/erpnext/support/web_form/issues/issues.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py
index c00dfa9..0c1ea16 100644
--- a/erpnext/telephony/doctype/call_log/call_log.py
+++ b/erpnext/telephony/doctype/call_log/call_log.py
@@ -3,14 +3,15 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from frappe.model.document import Document
-from erpnext.crm.doctype.utils import get_scheduled_employees_for_popup, strip_number
from frappe.contacts.doctype.contact.contact import get_contact_with_phone_number
from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links
+from frappe.model.document import Document
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']
@@ -173,4 +174,3 @@
})
return timeline_contents
-
diff --git a/erpnext/telephony/doctype/call_log/test_call_log.py b/erpnext/telephony/doctype/call_log/test_call_log.py
index faa6304..f8d458d 100644
--- a/erpnext/telephony/doctype/call_log/test_call_log.py
+++ b/erpnext/telephony/doctype/call_log/test_call_log.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestCallLog(unittest.TestCase):
pass
diff --git a/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py b/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py
index fcf2974..f48c808 100644
--- a/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py
+++ b/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class IncomingCallHandlingSchedule(Document):
pass
diff --git a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js
index 1bcc846..b80acdb 100644
--- a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js
+++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js
@@ -99,4 +99,3 @@
validate_call_schedule(frm.doc.call_handling_schedule);
}
});
-
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 2b2008a..faeff90 100644
--- a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
+++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py
@@ -3,11 +3,14 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
+
from datetime import datetime
from typing import Tuple
+
+import frappe
from frappe import _
+from frappe.model.document import Document
+
class IncomingCallSettings(Document):
def validate(self):
diff --git a/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py b/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py
index c058c11..243e3d9 100644
--- a/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py
+++ b/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestIncomingCallSettings(unittest.TestCase):
pass
diff --git a/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py b/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
index 85d6add..810b717 100644
--- a/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
+++ b/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestVoiceCallSettings(unittest.TestCase):
pass
diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
index ad3bbf1..fc0e338 100644
--- a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
+++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
@@ -3,8 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
# import frappe
from frappe.model.document import Document
+
class VoiceCallSettings(Document):
pass
diff --git a/erpnext/templates/emails/anniversary_reminder.html b/erpnext/templates/emails/anniversary_reminder.html
new file mode 100644
index 0000000..db338dd
--- /dev/null
+++ b/erpnext/templates/emails/anniversary_reminder.html
@@ -0,0 +1,25 @@
+<div class="gray-container text-center">
+ <div>
+ {% for person in anniversary_persons %}
+ {% if person.image %}
+ <img
+ class="avatar-frame standard-image"
+ src="{{ person.image }}"
+ style="{{ css_style or '' }}"
+ title="{{ person.name }}">
+ </span>
+ {% else %}
+ <span
+ class="avatar-frame standard-image"
+ style="{{ css_style or '' }}"
+ title="{{ person.name }}">
+ {{ frappe.utils.get_abbr(person.name) }}
+ </span>
+ {% endif %}
+ {% endfor %}
+ </div>
+ <div style="margin-top: 15px">
+ <span>{{ reminder_text }}</span>
+ <p class="text-muted">{{ message }}</p>
+ </div>
+</div>
diff --git a/erpnext/templates/emails/birthday_reminder.html b/erpnext/templates/emails/birthday_reminder.html
index 12cdf1e..1f57b49 100644
--- a/erpnext/templates/emails/birthday_reminder.html
+++ b/erpnext/templates/emails/birthday_reminder.html
@@ -22,4 +22,4 @@
<span>{{ reminder_text }}</span>
<p class="text-muted">{{ message }}</p>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/templates/emails/daily_project_summary.html b/erpnext/templates/emails/daily_project_summary.html
index 8b60830..5ccc610 100644
--- a/erpnext/templates/emails/daily_project_summary.html
+++ b/erpnext/templates/emails/daily_project_summary.html
@@ -43,4 +43,4 @@
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="20"></tr>
</table>
-{% endfor %}
\ No newline at end of file
+{% endfor %}
diff --git a/erpnext/templates/emails/daily_work_summary.html b/erpnext/templates/emails/daily_work_summary.html
index a22e09c..1764e8f 100644
--- a/erpnext/templates/emails/daily_work_summary.html
+++ b/erpnext/templates/emails/daily_work_summary.html
@@ -52,4 +52,4 @@
</div>
</tr>
</table>
-{% endif %}
\ No newline at end of file
+{% endif %}
diff --git a/erpnext/templates/emails/holiday_reminder.html b/erpnext/templates/emails/holiday_reminder.html
new file mode 100644
index 0000000..bbef6be
--- /dev/null
+++ b/erpnext/templates/emails/holiday_reminder.html
@@ -0,0 +1,16 @@
+<div>
+ <span>{{ reminder_text }}</span>
+ <p class="text-muted">{{ message }}</p>
+</div>
+
+{% if advance_holiday_reminder %}
+ {% if holidays | len > 0 %}
+ <ol>
+ {% for holiday in holidays %}
+ <li>{{ frappe.format(holiday.holiday_date, 'Date') }} - {{ holiday.description }}</li>
+ {% endfor %}
+ </ol>
+ {% else %}
+ <p>You have no upcoming holidays this {{ frequency }}.</p>
+ {% endif %}
+{% endif %}
diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html
index 812939a..3283987 100644
--- a/erpnext/templates/emails/request_for_quotation.html
+++ b/erpnext/templates/emails/request_for_quotation.html
@@ -21,4 +21,4 @@
</button>
</p>
-{% endif %}
\ No newline at end of file
+{% endif %}
diff --git a/erpnext/templates/emails/training_event.html b/erpnext/templates/emails/training_event.html
index 51c232d..8a2414a 100644
--- a/erpnext/templates/emails/training_event.html
+++ b/erpnext/templates/emails/training_event.html
@@ -11,7 +11,7 @@
<h4>{{_("Update Response")}}</h4>
{% if not self_study %}
<p>{{_("Please update your status for this training event")}}:</p>
-<form action="{{ confirm_link }}"><input style="display:inline-block" type="submit" value="Confirm Attendance" /></form>
+<form action="{{ confirm_link }}"><input style="display:inline-block" type="submit" value="Confirm Attendance" /></form>
<form action="{{ reject_link }}"><input style="display:inline-block" type="submit" value="Reject Invitation" /></form>
{% else %}
<p>{{_("Please confirm once you have completed your training")}}:</p>
diff --git a/erpnext/templates/generators/item/item_inquiry.js b/erpnext/templates/generators/item/item_inquiry.js
index e7db3a3..4724b68 100644
--- a/erpnext/templates/generators/item/item_inquiry.js
+++ b/erpnext/templates/generators/item/item_inquiry.js
@@ -74,4 +74,4 @@
d.show();
});
-});
\ No newline at end of file
+});
diff --git a/erpnext/templates/generators/item/item_specifications.html b/erpnext/templates/generators/item/item_specifications.html
index 469a45f..d4dfa8e 100644
--- a/erpnext/templates/generators/item/item_specifications.html
+++ b/erpnext/templates/generators/item/item_specifications.html
@@ -11,4 +11,4 @@
</table>
</div>
</div>
-{%- endif %}
\ No newline at end of file
+{%- endif %}
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index 9050cc3..b5f18ba 100644
--- a/erpnext/templates/generators/item_group.html
+++ b/erpnext/templates/generators/item_group.html
@@ -159,4 +159,4 @@
});
});
</script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/generators/job_opening.html b/erpnext/templates/generators/job_opening.html
index c562db3..135fb36 100644
--- a/erpnext/templates/generators/job_opening.html
+++ b/erpnext/templates/generators/job_opening.html
@@ -14,17 +14,17 @@
<div>{{ description }}</div>
{% endif %}
-{%- if publish_salary_range -%}
+{%- if publish_salary_range -%}
<div><b>{{_("Salary range per month")}}: </b>{{ frappe.format_value(frappe.utils.flt(lower_range), currency=currency) }} - {{ frappe.format_value(frappe.utils.flt(upper_range), currency=currency) }}</div>
{% endif %}
<p style='margin-top: 30px'>
{%- if job_application_route -%}
- <a class='btn btn-primary'
+ <a class='btn btn-primary'
href='/{{job_application_route}}?new=1&job_title={{ doc.name }}'>
{{ _("Apply Now") }}</a>
{% else %}
- <a class='btn btn-primary'
+ <a class='btn btn-primary'
href='/job_application?new=1&job_title={{ doc.name }}'>
{{ _("Apply Now") }}</a>
{% endif %}
diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html
index 8b15344..8cc58a0 100644
--- a/erpnext/templates/generators/student_admission.html
+++ b/erpnext/templates/generators/student_admission.html
@@ -14,7 +14,7 @@
{%- if introduction -%}
<div>{{ introduction }}</div>
-{% endif %}
+{% endif %}
{%- if doc.enable_admission_application -%}
<p>
diff --git a/erpnext/templates/includes/cart/address_picker_card.html b/erpnext/templates/includes/cart/address_picker_card.html
index 2334ea2..646210e 100644
--- a/erpnext/templates/includes/cart/address_picker_card.html
+++ b/erpnext/templates/includes/cart/address_picker_card.html
@@ -9,4 +9,4 @@
</p>
<a href="/addresses?name={{address.name}}" class="card-link">{{ _('Edit') }}</a>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/templates/includes/cart/cart_address_picker.html b/erpnext/templates/includes/cart/cart_address_picker.html
index 72cc5f5..66a50ec 100644
--- a/erpnext/templates/includes/cart/cart_address_picker.html
+++ b/erpnext/templates/includes/cart/cart_address_picker.html
@@ -1,4 +1,3 @@
<div class="mb-3 frappe-card p-5" data-section="shipping-address">
<h6>{{ _("Shipping Address") }}</h6>
</div>
-
diff --git a/erpnext/templates/includes/cart/cart_items_dropdown.html b/erpnext/templates/includes/cart/cart_items_dropdown.html
index b2ba431..5d107fc 100644
--- a/erpnext/templates/includes/cart/cart_items_dropdown.html
+++ b/erpnext/templates/includes/cart/cart_items_dropdown.html
@@ -9,4 +9,4 @@
{{ d.get_formatted("amount") }}
</div>
</div>
-{% endfor %}
\ No newline at end of file
+{% endfor %}
diff --git a/erpnext/templates/includes/course/macros.html b/erpnext/templates/includes/course/macros.html
index c80dca4..334b5ea 100644
--- a/erpnext/templates/includes/course/macros.html
+++ b/erpnext/templates/includes/course/macros.html
@@ -1 +1 @@
-{% macro back_link(doc) %}&back-to=/courses?course={{ doc.name }}&back-to-title={{ doc.course_name }}{% endmacro %}
\ No newline at end of file
+{% macro back_link(doc) %}&back-to=/courses?course={{ doc.name }}&back-to-title={{ doc.course_name }}{% endmacro %}
diff --git a/erpnext/templates/includes/itemised_tax_breakup.html b/erpnext/templates/includes/itemised_tax_breakup.html
index c2f1353..5652bb1 100644
--- a/erpnext/templates/includes/itemised_tax_breakup.html
+++ b/erpnext/templates/includes/itemised_tax_breakup.html
@@ -43,4 +43,4 @@
{% endfor %}
</tbody>
</table>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index c44bfb5..be0d47f 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -120,4 +120,4 @@
{% endif %}
</div>
<a href="/{{ url or '#' }}" class="stretched-link"></a>
-{%- endmacro -%}
\ No newline at end of file
+{%- endmacro -%}
diff --git a/erpnext/templates/includes/navbar/navbar_items.html b/erpnext/templates/includes/navbar/navbar_items.html
index 133d99e..2912206 100644
--- a/erpnext/templates/includes/navbar/navbar_items.html
+++ b/erpnext/templates/includes/navbar/navbar_items.html
@@ -9,4 +9,4 @@
<span class="badge badge-primary cart-badge" id="cart-count"></span>
</a>
</li>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html
index da4fb8c..7b3c9a4 100644
--- a/erpnext/templates/includes/order/order_macros.html
+++ b/erpnext/templates/includes/order/order_macros.html
@@ -40,4 +40,4 @@
</div>
</div>
</div>
-{% endmacro %}
\ No newline at end of file
+{% endmacro %}
diff --git a/erpnext/templates/includes/projects.css b/erpnext/templates/includes/projects.css
index 5a717fc..5d9fc50 100644
--- a/erpnext/templates/includes/projects.css
+++ b/erpnext/templates/includes/projects.css
@@ -86,4 +86,4 @@
.progress-hg{
margin-bottom: 30!important;
height:2px;
-}
\ No newline at end of file
+}
diff --git a/erpnext/templates/includes/projects/project_search_box.html b/erpnext/templates/includes/projects/project_search_box.html
index 6f53bae..d746687 100644
--- a/erpnext/templates/includes/projects/project_search_box.html
+++ b/erpnext/templates/includes/projects/project_search_box.html
@@ -27,4 +27,4 @@
});
$(".form-search").on("submit", function() { return false; });
});
-</script>
\ No newline at end of file
+</script>
diff --git a/erpnext/templates/includes/rfq/rfq_items.html b/erpnext/templates/includes/rfq/rfq_items.html
index caa15f3..04cf922 100644
--- a/erpnext/templates/includes/rfq/rfq_items.html
+++ b/erpnext/templates/includes/rfq/rfq_items.html
@@ -1,4 +1,4 @@
-{% from "erpnext/templates/includes/rfq/rfq_macros.html" import item_name_and_description %}
+{% from "templates/includes/rfq/rfq_macros.html" import item_name_and_description %}
{% for d in doc.items %}
<div class="rfq-item">
diff --git a/erpnext/templates/includes/salary_slip_log.html b/erpnext/templates/includes/salary_slip_log.html
index 107df51..d36ee6e 100644
--- a/erpnext/templates/includes/salary_slip_log.html
+++ b/erpnext/templates/includes/salary_slip_log.html
@@ -16,4 +16,4 @@
</tr>
{% endfor %}
</tbody>
-</table>
\ No newline at end of file
+</table>
diff --git a/erpnext/templates/includes/topic/topic_row.html b/erpnext/templates/includes/topic/topic_row.html
index 3401bd3..38d46b7 100644
--- a/erpnext/templates/includes/topic/topic_row.html
+++ b/erpnext/templates/includes/topic/topic_row.html
@@ -1,4 +1,4 @@
-<div class="web-list-item">
+<div class="web-list-item">
<div class="row">
<a href = "/topics?topic={{ doc.name }}">
<div class="col-xs-12">
@@ -6,4 +6,4 @@
</div>
</a>
</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/erpnext/templates/pages/cart.py b/erpnext/templates/pages/cart.py
index 30b0357..7c441f7 100644
--- a/erpnext/templates/pages/cart.py
+++ b/erpnext/templates/pages/cart.py
@@ -4,8 +4,9 @@
no_cache = 1
-import frappe
+
from erpnext.shopping_cart.cart import get_cart_quotation
+
def get_context(context):
context.update(get_cart_quotation())
diff --git a/erpnext/templates/pages/cart_terms.html b/erpnext/templates/pages/cart_terms.html
index 521c583..6d84fb8 100644
--- a/erpnext/templates/pages/cart_terms.html
+++ b/erpnext/templates/pages/cart_terms.html
@@ -1,2 +1,2 @@
-<div>{{doc.terms}}</div>
\ No newline at end of file
+<div>{{doc.terms}}</div>
diff --git a/erpnext/templates/pages/courses.html b/erpnext/templates/pages/courses.html
index 42e7f3e..6592f7a 100644
--- a/erpnext/templates/pages/courses.html
+++ b/erpnext/templates/pages/courses.html
@@ -8,4 +8,4 @@
<p class="post-description"> {{ intro }} </p>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/courses.py b/erpnext/templates/pages/courses.py
index c80d8e7..7eb0135 100644
--- a/erpnext/templates/pages/courses.py
+++ b/erpnext/templates/pages/courses.py
@@ -2,8 +2,8 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
def get_context(context):
@@ -17,4 +17,3 @@
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 4ce2b31..366b283 100644
--- a/erpnext/templates/pages/help.py
+++ b/erpnext/templates/pages/help.py
@@ -1,8 +1,11 @@
from __future__ import unicode_literals
-import frappe, json
+import json
+
+import frappe
import requests
+
def get_context(context):
context.no_cache = 1
settings = frappe.get_doc("Support Settings", "Support Settings")
diff --git a/erpnext/templates/pages/home.css b/erpnext/templates/pages/home.css
index cf54766..785d805 100644
--- a/erpnext/templates/pages/home.css
+++ b/erpnext/templates/pages/home.css
@@ -6,4 +6,4 @@
padding: 10rem 0;
}
{% endif %}
-/* csslint ignore:end */
\ No newline at end of file
+/* csslint ignore:end */
diff --git a/erpnext/templates/pages/home.html b/erpnext/templates/pages/home.html
index 2ef9c10..9a61eab 100644
--- a/erpnext/templates/pages/home.html
+++ b/erpnext/templates/pages/home.html
@@ -72,4 +72,4 @@
{{ render_homepage_section(section) }}
{% endfor %}
</main>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/home.py b/erpnext/templates/pages/home.py
index 1c14450..97a66fc 100644
--- a/erpnext/templates/pages/home.py
+++ b/erpnext/templates/pages/home.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
no_cache = 1
diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.html b/erpnext/templates/pages/integrations/gocardless_checkout.html
index 7193d75..6072db4 100644
--- a/erpnext/templates/pages/integrations/gocardless_checkout.html
+++ b/erpnext/templates/pages/integrations/gocardless_checkout.html
@@ -13,4 +13,4 @@
<span class='gocardless-loading'>{{ _("Loading Payment System") }}</span>
</p>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.py b/erpnext/templates/pages/integrations/gocardless_checkout.py
index 96a0f42..2661a96 100644
--- a/erpnext/templates/pages/integrations/gocardless_checkout.py
+++ b/erpnext/templates/pages/integrations/gocardless_checkout.py
@@ -1,12 +1,17 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
+import json
+
import frappe
from frappe import _
-from frappe.utils import flt
-import json
-from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import gocardless_initialization, get_gateway_controller
-from frappe.utils import get_url
+from frappe.utils import flt, get_url
+
+from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import (
+ get_gateway_controller,
+ gocardless_initialization,
+)
no_cache = 1
@@ -74,4 +79,4 @@
except Exception as e:
frappe.log_error(e, "GoCardless Payment Error")
- return {"redirect_to": '/integrations/payment-failed'}
\ No newline at end of file
+ return {"redirect_to": '/integrations/payment-failed'}
diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.html b/erpnext/templates/pages/integrations/gocardless_confirmation.html
index 6ba154a..d961c63 100644
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.html
+++ b/erpnext/templates/pages/integrations/gocardless_confirmation.html
@@ -13,4 +13,4 @@
<span class='gocardless-loading'>{{ _("Payment Confirmation") }}</span>
</p>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.py b/erpnext/templates/pages/integrations/gocardless_confirmation.py
index cfaa1a1..35c8b90 100644
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.py
+++ b/erpnext/templates/pages/integrations/gocardless_confirmation.py
@@ -1,9 +1,14 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import gocardless_initialization, get_gateway_controller
+
+from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import (
+ get_gateway_controller,
+ gocardless_initialization,
+)
no_cache = 1
@@ -86,4 +91,4 @@
}).insert(ignore_permissions=True)
except Exception:
- frappe.log_error(frappe.get_traceback())
\ No newline at end of file
+ frappe.log_error(frappe.get_traceback())
diff --git a/erpnext/templates/pages/material_request_info.html b/erpnext/templates/pages/material_request_info.html
index 0c2772e..151d029 100644
--- a/erpnext/templates/pages/material_request_info.html
+++ b/erpnext/templates/pages/material_request_info.html
@@ -71,4 +71,4 @@
{% endfor %}
</div>
</div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/material_request_info.py b/erpnext/templates/pages/material_request_info.py
index 28e541a..c18e201 100644
--- a/erpnext/templates/pages/material_request_info.py
+++ b/erpnext/templates/pages/material_request_info.py
@@ -2,11 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
-
from frappe.utils import flt
+
def get_context(context):
context.no_cache = 1
context.show_sidebar = True
@@ -19,7 +20,7 @@
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")
if default_print_format:
context.print_format = default_print_format
@@ -45,5 +46,5 @@
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])
- return items
\ No newline at end of file
+ (material_request, item.item_code))[0][0])
+ return items
diff --git a/erpnext/templates/pages/non_profit/join-chapter.html b/erpnext/templates/pages/non_profit/join-chapter.html
index 89a7d2a..4923efc 100644
--- a/erpnext/templates/pages/non_profit/join-chapter.html
+++ b/erpnext/templates/pages/non_profit/join-chapter.html
@@ -56,4 +56,4 @@
{% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/non_profit/join_chapter.py b/erpnext/templates/pages/non_profit/join_chapter.py
index aa54a58..a1d1893 100644
--- a/erpnext/templates/pages/non_profit/join_chapter.py
+++ b/erpnext/templates/pages/non_profit/join_chapter.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def get_context(context):
context.no_cache = True
chapter = frappe.get_doc('Chapter', frappe.form_dict.name)
diff --git a/erpnext/templates/pages/non_profit/leave-chapter.html b/erpnext/templates/pages/non_profit/leave-chapter.html
index bc4242f..fd7658b 100644
--- a/erpnext/templates/pages/non_profit/leave-chapter.html
+++ b/erpnext/templates/pages/non_profit/leave-chapter.html
@@ -39,4 +39,4 @@
});
})
</script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/non_profit/leave_chapter.py b/erpnext/templates/pages/non_profit/leave_chapter.py
index 21cb722..ebdb664 100644
--- a/erpnext/templates/pages/non_profit/leave_chapter.py
+++ b/erpnext/templates/pages/non_profit/leave_chapter.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def get_context(context):
context.no_cache = True
chapter = frappe.get_doc('Chapter', frappe.form_dict.name)
diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py
index 34985d9..d4e81ab 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -2,10 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe import _
-from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import show_attachments
+
+from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
+ show_attachments,
+)
+
def get_context(context):
context.no_cache = 1
@@ -32,11 +36,13 @@
if not frappe.has_website_permission(context.doc):
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
+ 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)
context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
diff --git a/erpnext/templates/pages/partners.py b/erpnext/templates/pages/partners.py
index a7e60e2..b1c668a 100644
--- a/erpnext/templates/pages/partners.py
+++ b/erpnext/templates/pages/partners.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
page_title = "Partners"
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
index d0d72f0..1b9df2b 100644
--- a/erpnext/templates/pages/product_search.py
+++ b/erpnext/templates/pages/product_search.py
@@ -2,8 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-from frappe.utils import cstr, nowdate, cint
+from frappe.utils import cint, cstr, nowdate
+
from erpnext.setup.doctype.item_group.item_group import get_item_for_list_in_html
from erpnext.shopping_cart.product_info import set_product_info_for_website
@@ -47,4 +49,3 @@
set_product_info_for_website(item)
return [get_item_for_list_in_html(r) for r in data]
-
diff --git a/erpnext/templates/pages/projects.js b/erpnext/templates/pages/projects.js
index 262167f..bd6bcea 100644
--- a/erpnext/templates/pages/projects.js
+++ b/erpnext/templates/pages/projects.js
@@ -117,4 +117,4 @@
})
return false;
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/templates/pages/projects.py b/erpnext/templates/pages/projects.py
index 7ff4954..cabf37b 100644
--- a/erpnext/templates/pages/projects.py
+++ b/erpnext/templates/pages/projects.py
@@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
-import json
+
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)
diff --git a/erpnext/templates/pages/regional/india/update_gstin.py b/erpnext/templates/pages/regional/india/update_gstin.py
index f555db0..a8d03d5 100644
--- a/erpnext/templates/pages/regional/india/update_gstin.py
+++ b/erpnext/templates/pages/regional/india/update_gstin.py
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
+
import frappe
-from frappe import _
from six import iteritems
+
def get_context(context):
context.no_cache = 1
party = frappe.form_dict.party
diff --git a/erpnext/templates/pages/rfq.html b/erpnext/templates/pages/rfq.html
index 6e2edb6..6516482 100644
--- a/erpnext/templates/pages/rfq.html
+++ b/erpnext/templates/pages/rfq.html
@@ -86,7 +86,7 @@
<span class="small gray">{{d.transaction_date}}</span>
</div>
</div>
- <a class="transaction-item-link" href="/quotations/{{d.name}}">Link</a>
+ <a class="transaction-item-link" href="/supplier-quotations/{{d.name}}">Link</a>
</div>
{% endfor %}
</div>
@@ -95,6 +95,4 @@
</div>
</div>
</div>
-
-
{% endblock %}
diff --git a/erpnext/templates/pages/rfq.py b/erpnext/templates/pages/rfq.py
index 67679a1..b9f646b 100644
--- a/erpnext/templates/pages/rfq.py
+++ b/erpnext/templates/pages/rfq.py
@@ -2,11 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import formatdate
+
from erpnext.controllers.website_list_for_contact import get_customers_suppliers
+
def get_context(context):
context.no_cache = 1
context.show_sidebar = True
diff --git a/erpnext/templates/pages/search_help.py b/erpnext/templates/pages/search_help.py
index 887d8f4..4272b94 100644
--- a/erpnext/templates/pages/search_help.py
+++ b/erpnext/templates/pages/search_help.py
@@ -1,11 +1,14 @@
from __future__ import unicode_literals
-import frappe, requests
+
+import frappe
+import requests
from frappe import _
-from jinja2 import utils
-from html2text import html2text
-from six import text_type
from frappe.utils import sanitize_html
from frappe.utils.global_search import search
+from html2text import html2text
+from jinja2 import utils
+from six import text_type
+
def get_context(context):
context.no_cache = 1
diff --git a/erpnext/templates/pages/task_info.html b/erpnext/templates/pages/task_info.html
index 6cd6a7e..fe4d304 100644
--- a/erpnext/templates/pages/task_info.html
+++ b/erpnext/templates/pages/task_info.html
@@ -147,4 +147,4 @@
});
</script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/task_info.py b/erpnext/templates/pages/task_info.py
index b832b88..f219c3d 100644
--- a/erpnext/templates/pages/task_info.py
+++ b/erpnext/templates/pages/task_info.py
@@ -1,14 +1,14 @@
from __future__ import unicode_literals
+
import frappe
-from frappe import _
def get_context(context):
context.no_cache = 1
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.doc = task
\ No newline at end of file
+
+ context.doc = task
diff --git a/erpnext/templates/pages/timelog_info.html b/erpnext/templates/pages/timelog_info.html
index 22ea3e4..be13826 100644
--- a/erpnext/templates/pages/timelog_info.html
+++ b/erpnext/templates/pages/timelog_info.html
@@ -45,4 +45,4 @@
</div>
</div>
</div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/templates/pages/timelog_info.py b/erpnext/templates/pages/timelog_info.py
index 7a3361c..e0fb60d 100644
--- a/erpnext/templates/pages/timelog_info.py
+++ b/erpnext/templates/pages/timelog_info.py
@@ -1,11 +1,11 @@
from __future__ import unicode_literals
+
import frappe
-from frappe import _
def get_context(context):
context.no_cache = 1
timelog = frappe.get_doc('Time Log', frappe.form_dict.timelog)
-
- context.doc = timelog
\ No newline at end of file
+
+ context.doc = timelog
diff --git a/erpnext/templates/print_formats/includes/item_table_qty.html b/erpnext/templates/print_formats/includes/item_table_qty.html
index 8e68f1c..aaa9491 100644
--- a/erpnext/templates/print_formats/includes/item_table_qty.html
+++ b/erpnext/templates/print_formats/includes/item_table_qty.html
@@ -12,4 +12,3 @@
{%- endif %}
{{ doc.get_formatted("qty", doc) }}
{%- endif %}
-
diff --git a/erpnext/templates/print_formats/includes/total.html b/erpnext/templates/print_formats/includes/total.html
index 8179980..879203b 100644
--- a/erpnext/templates/print_formats/includes/total.html
+++ b/erpnext/templates/print_formats/includes/total.html
@@ -7,7 +7,7 @@
</div>
{% else %}
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
- <label>{{ _(doc.meta.get_label('total')) }}</label></div>
+ <label>{{ _(df.label) }}</label></div>
<div class="col-xs-7 text-right">
{{ doc.get_formatted("total", doc) }}
</div>
diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py
index abc04a8..dfba034 100644
--- a/erpnext/tests/test_init.py
+++ b/erpnext/tests/test_init.py
@@ -1,10 +1,12 @@
from __future__ import unicode_literals
+
import unittest
import frappe
-from erpnext import encode_company_abbr
from six.moves import range
+from erpnext import encode_company_abbr
+
test_records = frappe.get_test_records('Company')
class TestInit(unittest.TestCase):
diff --git a/erpnext/tests/test_notifications.py b/erpnext/tests/test_notifications.py
index 1fd90be..5fd9582 100644
--- a/erpnext/tests/test_notifications.py
+++ b/erpnext/tests/test_notifications.py
@@ -2,11 +2,12 @@
# MIT License. See license.txt
from __future__ import unicode_literals
-import frappe
+
import unittest
+import frappe
from frappe.desk import notifications
-from frappe.test_runner import make_test_objects
+
class TestNotifications(unittest.TestCase):
def test_get_notifications_for_targets(self):
diff --git a/erpnext/tests/test_regional.py b/erpnext/tests/test_regional.py
index 282fc64..fe848a3 100644
--- a/erpnext/tests/test_regional.py
+++ b/erpnext/tests/test_regional.py
@@ -1,5 +1,11 @@
from __future__ import unicode_literals
-import unittest, frappe, erpnext
+
+import unittest
+
+import frappe
+
+import erpnext
+
@erpnext.allow_regional
def test_method():
@@ -14,4 +20,4 @@
self.assertEqual(test_method(), 'original')
frappe.flags.country = 'France'
- self.assertEqual(test_method(), 'overridden')
\ No newline at end of file
+ self.assertEqual(test_method(), 'overridden')
diff --git a/erpnext/tests/test_search.py b/erpnext/tests/test_search.py
index f60e5e4..a6619b2 100644
--- a/erpnext/tests/test_search.py
+++ b/erpnext/tests/test_search.py
@@ -1,8 +1,11 @@
from __future__ import unicode_literals
+
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):
diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py
index 8b0ce09..e45f098 100644
--- a/erpnext/tests/test_subcontracting.py
+++ b/erpnext/tests/test_subcontracting.py
@@ -1,16 +1,24 @@
from __future__ import unicode_literals
-import frappe
-import unittest
+
import copy
-from frappe.utils import cint
+import unittest
from collections import defaultdict
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+import frappe
+from frappe.utils import cint
+
+from erpnext.buying.doctype.purchase_order.purchase_order import (
+ get_materials_from_supplier,
+ make_purchase_invoice,
+ make_purchase_receipt,
+ make_rm_stock_entry,
+)
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.buying.doctype.purchase_order.purchase_order import (make_rm_stock_entry,
- make_purchase_receipt, make_purchase_invoice, get_materials_from_supplier)
+from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
class TestSubcontracting(unittest.TestCase):
def setUp(self):
@@ -874,4 +882,4 @@
def set_backflush_based_on(based_on):
frappe.db.set_value('Buying Settings', None,
- 'backflush_raw_materials_of_subcontract_based_on', based_on)
\ No newline at end of file
+ 'backflush_raw_materials_of_subcontract_based_on', based_on)
diff --git a/erpnext/tests/test_webform.py b/erpnext/tests/test_webform.py
new file mode 100644
index 0000000..19255db
--- /dev/null
+++ b/erpnext/tests/test_webform.py
@@ -0,0 +1,138 @@
+import unittest
+
+import frappe
+
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+
+
+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_custom_doctype()
+ create_webform()
+ 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()])
+
+ 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'])
+
+ 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'])
+
+ frappe.set_user("Administrator")
+
+def get_data():
+ 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')
+ 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)
+
+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)
+
+ 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.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)
+
+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'
+ }
+ ]
+
+ }).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
diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py
index df715ab..881f286 100644
--- a/erpnext/tests/test_woocommerce.py
+++ b/erpnext/tests/test_woocommerce.py
@@ -1,7 +1,15 @@
from __future__ import unicode_literals
-import unittest, frappe, requests, os, time, erpnext
+
+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):
if not frappe.db.exists('Company', 'Woocommerce'):
diff --git a/erpnext/tests/ui/setup_wizard.js b/erpnext/tests/ui/setup_wizard.js
index aeb8d2a..ccff785 100644
--- a/erpnext/tests/ui/setup_wizard.js
+++ b/erpnext/tests/ui/setup_wizard.js
@@ -44,4 +44,4 @@
after: browser => {
browser.end();
},
-};
\ No newline at end of file
+};
diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py
new file mode 100644
index 0000000..9c8c371
--- /dev/null
+++ b/erpnext/tests/ui_test_helpers.py
@@ -0,0 +1,62 @@
+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 11eb6af..a3cab4b 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -3,8 +3,14 @@
import copy
from contextlib import contextmanager
+from typing import Any, Dict, NewType, Optional
import frappe
+from frappe.core.doctype.report.report import get_report_module_dotted_path
+
+ReportFilters = Dict[str, Any]
+ReportName = NewType("ReportName", str)
+
def create_test_contact_and_address():
frappe.db.sql('delete from tabContact')
@@ -77,3 +83,39 @@
for key, value in previous_settings.items():
setattr(settings, key, value)
settings.save()
+
+
+def execute_script_report(
+ 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.
+ """
+
+ if default_filters is None:
+ default_filters = {}
+
+ report_execute_fn = frappe.get_attr(get_report_module_dotted_path(module, report_name) + ".execute")
+ report_filters = frappe._dict(default_filters).copy().update(filters)
+
+ report_data = report_execute_fn(report_filters)
+
+ if optional_filters:
+ for key, value in optional_filters.items():
+ filter_with_optional_param = report_filters.copy().update({key: value})
+ report_execute_fn(filter_with_optional_param)
+
+ return report_data
diff --git a/erpnext/utilities/__init__.py b/erpnext/utilities/__init__.py
index 0a5aa3c..ca8bc19 100644
--- a/erpnext/utilities/__init__.py
+++ b/erpnext/utilities/__init__.py
@@ -1,9 +1,12 @@
## temp utility
from __future__ import print_function, unicode_literals
+
import frappe
-from erpnext.utilities.activation import get_level
from frappe.utils import cstr
+from erpnext.utilities.activation import get_level
+
+
def update_doctypes():
for d in frappe.db.sql("""select df.parent, df.fieldname
from tabDocField df, tabDocType dt where df.fieldname
diff --git a/erpnext/utilities/activation.py b/erpnext/utilities/activation.py
index 50c4b25..c21bff0 100644
--- a/erpnext/utilities/activation.py
+++ b/erpnext/utilities/activation.py
@@ -2,44 +2,47 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+import frappe
from frappe import _
from six import iteritems
+import erpnext
+
+
def get_level():
activation_level = 0
sales_data = []
min_count = 0
doctypes = {
"Asset": 5,
- "BOM": 3,
- "Customer": 5,
+ "BOM": 3,
+ "Customer": 5,
"Delivery Note": 5,
- "Employee": 3,
- "Instructor": 5,
+ "Employee": 3,
+ "Instructor": 5,
"Issue": 5,
- "Item": 5,
- "Journal Entry": 3,
+ "Item": 5,
+ "Journal Entry": 3,
"Lead": 3,
"Leave Application": 5,
"Material Request": 5,
- "Opportunity": 5,
- "Payment Entry": 2,
+ "Opportunity": 5,
+ "Payment Entry": 2,
"Project": 5,
- "Purchase Order": 2,
+ "Purchase Order": 2,
"Purchase Invoice": 5,
"Purchase Receipt": 5,
"Quotation": 3,
"Salary Slip": 5,
"Salary Structure": 5,
- "Sales Order": 2,
- "Sales Invoice": 2,
+ "Sales Order": 2,
+ "Sales Invoice": 2,
"Stock Entry": 3,
- "Student": 5,
+ "Student": 5,
"Supplier": 5,
"Task": 5,
- "User": 5,
+ "User": 5,
"Work Order": 5
}
diff --git a/erpnext/utilities/bot.py b/erpnext/utilities/bot.py
index b2e74da..9e830a2 100644
--- a/erpnext/utilities/bot.py
+++ b/erpnext/utilities/bot.py
@@ -3,10 +3,10 @@
from __future__ import unicode_literals
-from frappe.utils.bot import BotParser
-
import frappe
from frappe import _
+from frappe.utils.bot import BotParser
+
class FindItemBot(BotParser):
def get_reply(self):
@@ -36,4 +36,4 @@
return "\n\n".join(out)
else:
- return _("Did not find any item called {0}").format(item)
\ No newline at end of file
+ return _("Did not find any item called {0}").format(item)
diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.py b/erpnext/utilities/doctype/rename_tool/rename_tool.py
index 0f8a7a3..8377cec 100644
--- a/erpnext/utilities/doctype/rename_tool/rename_tool.py
+++ b/erpnext/utilities/doctype/rename_tool/rename_tool.py
@@ -4,11 +4,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+import frappe
from frappe.model.document import Document
from frappe.model.rename_doc import bulk_rename
+
class RenameTool(Document):
pass
@@ -29,4 +30,3 @@
rows = read_csv_content_from_attached_file(frappe.get_doc("Rename Tool", "Rename Tool"))
return bulk_rename(select_doctype, rows=rows)
-
diff --git a/erpnext/utilities/doctype/sms_log/sms_log.py b/erpnext/utilities/doctype/sms_log/sms_log.py
index e634f80..ce3cc46 100644
--- a/erpnext/utilities/doctype/sms_log/sms_log.py
+++ b/erpnext/utilities/doctype/sms_log/sms_log.py
@@ -2,9 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
from frappe.model.document import Document
+
class SMSLog(Document):
pass
diff --git a/erpnext/utilities/doctype/sms_log/test_sms_log.py b/erpnext/utilities/doctype/sms_log/test_sms_log.py
index 65f52b4..3baeb25 100644
--- a/erpnext/utilities/doctype/sms_log/test_sms_log.py
+++ b/erpnext/utilities/doctype/sms_log/test_sms_log.py
@@ -3,7 +3,6 @@
# See license.txt
from __future__ import unicode_literals
-import frappe
import unittest
# test_records = frappe.get_test_records('SMS Log')
diff --git a/erpnext/utilities/doctype/video/test_video.py b/erpnext/utilities/doctype/video/test_video.py
index 33ea31c..530136c 100644
--- a/erpnext/utilities/doctype/video/test_video.py
+++ b/erpnext/utilities/doctype/video/test_video.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestVideo(unittest.TestCase):
pass
diff --git a/erpnext/utilities/doctype/video/video.py b/erpnext/utilities/doctype/video/video.py
index c2e414e..d9907cf 100644
--- a/erpnext/utilities/doctype/video/video.py
+++ b/erpnext/utilities/doctype/video/video.py
@@ -3,14 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe
+
import re
-import pytz
-from frappe.model.document import Document
-from frappe import _
from datetime import datetime
-from six import string_types
+
+import frappe
+import pytz
+from frappe import _
+from frappe.model.document import Document
from pyyoutube import Api
+from six import string_types
+
class Video(Document):
def validate(self):
diff --git a/erpnext/utilities/doctype/video/video_list.js b/erpnext/utilities/doctype/video/video_list.js
index 8273a4a..6f78f6e 100644
--- a/erpnext/utilities/doctype/video/video_list.js
+++ b/erpnext/utilities/doctype/video/video_list.js
@@ -4,4 +4,4 @@
frappe.set_route("Form","Video Settings", "Video Settings");
});
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/utilities/doctype/video_settings/test_video_settings.py b/erpnext/utilities/doctype/video_settings/test_video_settings.py
index b217afe..e871435 100644
--- a/erpnext/utilities/doctype/video_settings/test_video_settings.py
+++ b/erpnext/utilities/doctype/video_settings/test_video_settings.py
@@ -6,5 +6,6 @@
# import frappe
import unittest
+
class TestVideoSettings(unittest.TestCase):
pass
diff --git a/erpnext/utilities/doctype/video_settings/video_settings.py b/erpnext/utilities/doctype/video_settings/video_settings.py
index 36fb54f..7209134 100644
--- a/erpnext/utilities/doctype/video_settings/video_settings.py
+++ b/erpnext/utilities/doctype/video_settings/video_settings.py
@@ -3,10 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
+from apiclient.discovery import build
from frappe import _
from frappe.model.document import Document
-from apiclient.discovery import build
+
class VideoSettings(Document):
def validate(self):
@@ -19,4 +21,4 @@
except Exception:
title = _("Failed to Authenticate the API key.")
frappe.log_error(title + "\n\n" + frappe.get_traceback(), title=title)
- frappe.throw(title + " Please check the error logs.", title=_("Invalid Credentials"))
\ No newline at end of file
+ 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
new file mode 100644
index 0000000..0e7f81f
--- /dev/null
+++ b/erpnext/utilities/hierarchy_chart.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe import _
+
+
+@frappe.whitelist()
+def get_all_nodes(method, company):
+ '''Recursively gets all data from nodes'''
+ method = frappe.get_attr(method)
+
+ if method not in frappe.whitelisted:
+ frappe.throw(_('Not Permitted'), frappe.PermissionError)
+
+ root_nodes = method(company=company)
+ result = []
+ nodes_to_expand = []
+
+ 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')])
+
+ 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))
+ for d in data:
+ if d.get('expandable'):
+ nodes_to_expand.append({'id': d.get('id'), 'name': d.get('name')})
+
+ return result
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index 70b4176..e567f77 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -4,10 +4,12 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cint, fmt_money, flt, nowdate, getdate
+from frappe.utils import cint, flt, fmt_money, getdate, nowdate
+
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
from erpnext.stock.doctype.batch.batch import get_batch_qty
+
def get_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"])
@@ -127,7 +129,7 @@
return price_obj
def get_non_stock_item_status(item_code, item_warehouse_field):
-#if item belongs to product bundle, check if bundle items are in stock
+ #if item belongs to product bundle, check if 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('Item', item_code, item_warehouse_field)
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
index 3516a35..50f3b68 100644
--- a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
@@ -2,10 +2,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+
import frappe
from frappe import _
from frappe.utils import flt
+
def execute(filters=None):
if not frappe.db.get_single_value("Video Settings", "enable_youtube_tracking") or not filters:
return [], []
@@ -110,4 +112,4 @@
"datatype": "Float",
}
]
- return chart_data, summary
\ No newline at end of file
+ return chart_data, summary
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index db99726..5153683 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -2,13 +2,15 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+
import frappe
import frappe.share
from frappe import _
-from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form, date_diff, nowdate
+from frappe.utils import cint, cstr, flt, get_time, now_datetime
+from six import string_types
+
from erpnext.controllers.status_updater import StatusUpdater
-from six import string_types
class UOMMustBeIntegerError(frappe.ValidationError): pass
diff --git a/erpnext/utilities/web_form/addresses/addresses.js b/erpnext/utilities/web_form/addresses/addresses.js
index 699703c..ffc5e98 100644
--- a/erpnext/utilities/web_form/addresses/addresses.js
+++ b/erpnext/utilities/web_form/addresses/addresses.js
@@ -1,3 +1,3 @@
frappe.ready(function() {
// bind events here
-})
\ No newline at end of file
+})
diff --git a/erpnext/utilities/web_form/addresses/addresses.py b/erpnext/utilities/web_form/addresses/addresses.py
index 3fd1017..4a8d3e1 100644
--- a/erpnext/utilities/web_form/addresses/addresses.py
+++ b/erpnext/utilities/web_form/addresses/addresses.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import frappe
def get_context(context):
# do your magic here
diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html
index 92c76ad..a7838ee 100644
--- a/erpnext/www/all-products/index.html
+++ b/erpnext/www/all-products/index.html
@@ -98,14 +98,14 @@
<div class="filter-options">
{% for attr_value in attribute.item_attribute_values %}
<div class="checkbox">
- <label data-value="{{ value }}">
+ <label>
<input type="checkbox"
class="product-filter attribute-filter"
- id="{{attr_value.name}}"
+ id="{{attr_value}}"
data-attribute-name="{{ attribute.name }}"
- data-attribute-value="{{ attr_value.attribute_value }}"
+ data-attribute-value="{{ attr_value }}"
{% if attr_value.checked %} checked {% endif %}>
- <span class="label-area">{{ attr_value.attribute_value }}</span>
+ <span class="label-area">{{ attr_value }}</span>
</label>
</div>
{% endfor %}
@@ -164,4 +164,4 @@
});
</script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py
index fd6400f..df5258b 100644
--- a/erpnext/www/all-products/index.py
+++ b/erpnext/www/all-products/index.py
@@ -1,8 +1,8 @@
import frappe
-from erpnext.portal.product_configurator.utils import (get_products_for_website, get_product_settings,
- get_field_filter_data, get_attribute_filter_data)
-from erpnext.shopping_cart.product_query import ProductQuery
+
+from erpnext.portal.product_configurator.utils import get_product_settings
from erpnext.shopping_cart.filters import ProductFiltersBuilder
+from erpnext.shopping_cart.product_query import ProductQuery
sitemap = 1
@@ -27,7 +27,7 @@
filter_engine = ProductFiltersBuilder()
context.field_filters = filter_engine.get_field_filters()
- context.attribute_filters = filter_engine.get_attribute_fitlers()
+ context.attribute_filters = filter_engine.get_attribute_filters()
context.product_settings = product_settings
context.body_class = "product-page"
diff --git a/erpnext/www/all-products/item_row.html b/erpnext/www/all-products/item_row.html
index 20fc9a4..a7e994c 100644
--- a/erpnext/www/all-products/item_row.html
+++ b/erpnext/www/all-products/item_row.html
@@ -4,4 +4,3 @@
item.item_name or item.name, item.website_image or item.image, item.route, item.website_description or item.description,
item.formatted_price, item.item_group
) }}
-
diff --git a/erpnext/www/all-products/not_found.html b/erpnext/www/all-products/not_found.html
index e1986b4..91989a9 100644
--- a/erpnext/www/all-products/not_found.html
+++ b/erpnext/www/all-products/not_found.html
@@ -1 +1 @@
-<div class="d-flex justify-content-center p-3 text-muted">{{ _('No products found') }}</div>
\ No newline at end of file
+<div class="d-flex justify-content-center p-3 text-muted">{{ _('No products found') }}</div>
diff --git a/erpnext/www/book_appointment/index.css b/erpnext/www/book_appointment/index.css
index 6c49fde..2776108 100644
--- a/erpnext/www/book_appointment/index.css
+++ b/erpnext/www/book_appointment/index.css
@@ -12,7 +12,7 @@
@media (max-width: 768px) {
#submit-button-area {
display: grid;
- grid-template-areas:
+ grid-template-areas:
"submit"
"back";
}
diff --git a/erpnext/www/book_appointment/index.html b/erpnext/www/book_appointment/index.html
index f242f43..207175f 100644
--- a/erpnext/www/book_appointment/index.html
+++ b/erpnext/www/book_appointment/index.html
@@ -63,4 +63,4 @@
</div>
</div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/www/book_appointment/index.py b/erpnext/www/book_appointment/index.py
index 4f45561..8cda3c1 100644
--- a/erpnext/www/book_appointment/index.py
+++ b/erpnext/www/book_appointment/index.py
@@ -1,6 +1,7 @@
-import frappe
import datetime
import json
+
+import frappe
import pytz
from frappe import _
diff --git a/erpnext/www/book_appointment/verify/index.html b/erpnext/www/book_appointment/verify/index.html
index ebb65b1..9bcd3d2 100644
--- a/erpnext/www/book_appointment/verify/index.html
+++ b/erpnext/www/book_appointment/verify/index.html
@@ -3,7 +3,7 @@
{% block title %}
{{ _("Verify Email") }}
{% endblock%}
-
+
{% block page_content %}
{% if success==True %}
@@ -15,4 +15,4 @@
Verification failed please check the link
</div>
{% endif %}
-{% endblock%}
\ No newline at end of file
+{% endblock%}
diff --git a/erpnext/www/book_appointment/verify/index.py b/erpnext/www/book_appointment/verify/index.py
index d4478ae..dc36f4f 100644
--- a/erpnext/www/book_appointment/verify/index.py
+++ b/erpnext/www/book_appointment/verify/index.py
@@ -1,6 +1,7 @@
import frappe
-
from frappe.utils.verified_command import verify_request
+
+
@frappe.whitelist(allow_guest=True)
def get_context(context):
if not verify_request():
@@ -17,4 +18,4 @@
return context
else:
context.success = False
- return context
\ No newline at end of file
+ return context
diff --git a/erpnext/www/lms/content.py b/erpnext/www/lms/content.py
index 0c04845..97f5918 100644
--- a/erpnext/www/lms/content.py
+++ b/erpnext/www/lms/content.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
-import erpnext.education.utils as utils
+
import frappe
+import erpnext.education.utils as utils
+
no_cache = 1
def get_context(context):
@@ -65,4 +67,4 @@
and `tabTopic Content`.parent = `tabCourse Topic`.topic
and `tabProgram Course`.parent = %(program)s""", {'program': program})
- return (content, content_type) in contents_of_program
\ No newline at end of file
+ return (content, content_type) in contents_of_program
diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html
index 0d70ed5..c07b940 100644
--- a/erpnext/www/lms/course.html
+++ b/erpnext/www/lms/course.html
@@ -103,4 +103,4 @@
</div>
</div>
</section>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/www/lms/course.py b/erpnext/www/lms/course.py
index c18d64e..1ec097b 100644
--- a/erpnext/www/lms/course.py
+++ b/erpnext/www/lms/course.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
-import erpnext.education.utils as utils
+
import frappe
+import erpnext.education.utils as utils
+
no_cache = 1
def get_context(context):
diff --git a/erpnext/www/lms/index.py b/erpnext/www/lms/index.py
index 26f59a2..c35fff2 100644
--- a/erpnext/www/lms/index.py
+++ b/erpnext/www/lms/index.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
-import erpnext.education.utils as utils
+
import frappe
+import erpnext.education.utils as utils
+
no_cache = 1
def get_context(context):
@@ -13,4 +15,4 @@
def get_featured_programs():
- return utils.get_portal_programs() or []
\ No newline at end of file
+ return utils.get_portal_programs() or []
diff --git a/erpnext/www/lms/macros/card.html b/erpnext/www/lms/macros/card.html
index dc8fc5c..3cbdec6 100644
--- a/erpnext/www/lms/macros/card.html
+++ b/erpnext/www/lms/macros/card.html
@@ -31,4 +31,4 @@
<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 %}
\ No newline at end of file
+{% endmacro %}
diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html
index 94f239e..e72bfc8 100644
--- a/erpnext/www/lms/macros/hero.html
+++ b/erpnext/www/lms/macros/hero.html
@@ -52,4 +52,4 @@
}
</script>
{% endblock %}
-{% endmacro %}
\ No newline at end of file
+{% endmacro %}
diff --git a/erpnext/www/lms/profile.py b/erpnext/www/lms/profile.py
index 4788ea6..d5dcd2b 100644
--- a/erpnext/www/lms/profile.py
+++ b/erpnext/www/lms/profile.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
-import erpnext.education.utils as utils
+
import frappe
+import erpnext.education.utils as utils
+
no_cache = 1
def get_context(context):
@@ -23,4 +25,4 @@
completion = utils.get_program_completion(program)
student_progress.append({'program': program.program_name, 'name': program.name, 'progress':progress, 'completion': completion})
- return student_progress
\ No newline at end of file
+ return student_progress
diff --git a/erpnext/www/lms/program.html b/erpnext/www/lms/program.html
index 7ad6186..30528c6 100644
--- a/erpnext/www/lms/program.html
+++ b/erpnext/www/lms/program.html
@@ -84,4 +84,4 @@
</div>
</div>
</section>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/www/lms/program.py b/erpnext/www/lms/program.py
index 104d3fa..9980d88 100644
--- a/erpnext/www/lms/program.py
+++ b/erpnext/www/lms/program.py
@@ -1,8 +1,10 @@
from __future__ import unicode_literals
-import erpnext.education.utils as utils
+
import frappe
from frappe import _
+import erpnext.education.utils as utils
+
no_cache = 1
def get_context(context):
@@ -26,4 +28,4 @@
def get_course_progress(courses, program):
progress = {course.name: utils.get_course_progress(course, program) for course in courses}
- return progress or {}
\ No newline at end of file
+ return progress or {}
diff --git a/erpnext/www/lms/topic.html b/erpnext/www/lms/topic.html
index cd24616..dc69599 100644
--- a/erpnext/www/lms/topic.html
+++ b/erpnext/www/lms/topic.html
@@ -55,4 +55,4 @@
</div>
</div>
</section>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/erpnext/www/lms/topic.py b/erpnext/www/lms/topic.py
index 8abbc72..ebedaf5 100644
--- a/erpnext/www/lms/topic.py
+++ b/erpnext/www/lms/topic.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
-import erpnext.education.utils as utils
+
import frappe
+import erpnext.education.utils as utils
+
no_cache = 1
def get_context(context):
@@ -42,4 +44,4 @@
result = None
progress.append({'content': content, 'content_type': content.doctype, 'completed': status, 'score': score, 'result': result})
- return progress
\ No newline at end of file
+ return progress
diff --git a/erpnext/www/payment_setup_certification.py b/erpnext/www/payment_setup_certification.py
index 6b02e4e..e509083 100644
--- a/erpnext/www/payment_setup_certification.py
+++ b/erpnext/www/payment_setup_certification.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+
import frappe
no_cache = 1
diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html
index 12b4c2c..3c19198 100644
--- a/erpnext/www/support/index.html
+++ b/erpnext/www/support/index.html
@@ -96,6 +96,6 @@
.search-container {
margin-top: 1.2rem;
max-width: 500px;
- }
+ }
</style>
{%- endblock -%}
diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py
index 5d26743..4857b0d 100644
--- a/erpnext/www/support/index.py
+++ b/erpnext/www/support/index.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
import frappe
+
def get_context(context):
context.no_cache = 1
context.align_greeting = ''
@@ -8,7 +10,7 @@
context.greeting_title = setting.greeting_title
context.greeting_subtitle = setting.greeting_subtitle
-
+
# Support content
favorite_articles = get_favorite_articles_by_page_view()
if len(favorite_articles) < 6:
@@ -16,15 +18,15 @@
if favorite_articles:
for article in favorite_articles:
name_list.append(article.name)
- for record in (frappe.get_all("Help Article",
- fields=["title", "content", "route", "category"],
- filters={"name": ['not in', tuple(name_list)], "published": 1},
+ 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)))):
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(
"""
@@ -34,13 +36,13 @@
t1.content as content,
t1.route as route,
t1.category as category,
- count(t1.route) as count
- FROM `tabHelp Article` AS t1
+ count(t1.route) as count
+ FROM `tabHelp Article` AS t1
INNER JOIN
- `tabWeb Page View` AS t2
- ON t1.route = t2.path
+ `tabWeb Page View` AS t2
+ ON t1.route = t2.path
WHERE t1.published = 1
- GROUP BY route
+ GROUP BY route
ORDER BY count DESC
LIMIT 6;
""", as_dict=True)
@@ -71,4 +73,4 @@
'articles': help_articles,
}
help_article_list.append(help_aricles_per_caetgory)
- return help_article_list
\ No newline at end of file
+ return help_article_list
diff --git a/package.json b/package.json
index c9ee7a6..6c11e9d 100644
--- a/package.json
+++ b/package.json
@@ -11,15 +11,9 @@
"bugs": {
"url": "https://github.com/frappe/erpnext/issues"
},
- "devDependencies": {
- "snyk": "^1.518.0"
- },
+ "devDependencies": {},
"dependencies": {
+ "html2canvas": "^1.1.4",
"onscan.js": "^1.5.2"
- },
- "scripts": {
- "snyk-protect": "snyk protect",
- "prepare": "yarn run snyk-protect"
- },
- "snyk": true
+ }
}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..8043dd9
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,11 @@
+[tool.black]
+line-length = 99
+
+[tool.isort]
+line_length = 99
+multi_line_output = 3
+include_trailing_comma = true
+force_grid_wrap = 0
+use_parentheses = true
+ensure_newline_before_comments = true
+indent = "\t"
diff --git a/yarn.lock b/yarn.lock
index 242695c..8e5d1bd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,3556 +2,26 @@
# yarn lockfile v1
-"@arcanis/slice-ansi@^1.0.2":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/@arcanis/slice-ansi/-/slice-ansi-1.0.2.tgz#35331e41a1062e3c53c01ad2ec1555c5c1959d8f"
- integrity sha512-lDL63z0W/L/WTgqrwVOuNyMAsTv+pvjybd21z9SWdStmQoXT59E/iVWwat3gYjcdTNBf6oHAMoyFm8dtjpXEYw==
- dependencies:
- grapheme-splitter "^1.0.4"
-
-"@deepcode/dcignore@^1.0.2":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/@deepcode/dcignore/-/dcignore-1.0.2.tgz#39e4a3df7dde8811925330506e4bb3fbf3c288d8"
- integrity sha512-DPgxtHuJwBORpqRkPXzzOT+uoPRVJmaN7LR+pmeL6DQM90kj6G6GFUH1i/YpRH8NbML8ZGEDwB9f9u4UwD2pzg==
-
-"@nodelib/fs.scandir@2.1.4":
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
- integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==
- dependencies:
- "@nodelib/fs.stat" "2.0.4"
- run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2":
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655"
- integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==
-
-"@nodelib/fs.walk@^1.2.3":
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063"
- integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==
- dependencies:
- "@nodelib/fs.scandir" "2.1.4"
- fastq "^1.6.0"
-
-"@octetstream/promisify@2.0.2":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@octetstream/promisify/-/promisify-2.0.2.tgz#29ac3bd7aefba646db670227f895d812c1a19615"
- integrity sha512-7XHoRB61hxsz8lBQrjC1tq/3OEIgpvGWg6DKAdwi7WRzruwkmsdwmOoUXbU4Dtd4RSOMDwed0SkP3y8UlMt1Bg==
-
-"@open-policy-agent/opa-wasm@^1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@open-policy-agent/opa-wasm/-/opa-wasm-1.2.0.tgz#481b766093f70b00efefbee1e4192f375fd34ca2"
- integrity sha512-CtUBTnzvDrT0NASa8IuGQTxFGgt2vxbLnMYuTA+uDFxOcA4uK4mGFgrhHJtxUZnWHiwemOvKKSY3BMCo7qiAsQ==
- dependencies:
- sprintf-js "^1.1.2"
- utf8 "^3.0.0"
-
-"@sindresorhus/is@^0.14.0":
- version "0.14.0"
- resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
- integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
-
-"@sindresorhus/is@^2.1.1":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1"
- integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==
-
-"@sindresorhus/is@^4.0.0":
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5"
- integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==
-
-"@snyk/cli-interface@2.11.0", "@snyk/cli-interface@^2.11.0", "@snyk/cli-interface@^2.9.1", "@snyk/cli-interface@^2.9.2":
- version "2.11.0"
- resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.11.0.tgz#9df68c8cd54de5dff69f0ab797a188541d9c8965"
- integrity sha512-T3xfDqrEFKclHGdJx4/5+D5F7e76/99f33guE4RTlVITBhy7VVnjz4t/NDr3UYqcC0MgAmiC4bSVYHnlshuwJw==
- dependencies:
- "@types/graphlib" "^2"
-
-"@snyk/cli-interface@^2.0.3":
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-2.3.1.tgz#73f2f4bd717b9f03f096ede3ff5830eb8d2f3716"
- integrity sha512-JZvsmhDXSyjv1dkc12lPI3tNTNYlIaOiIQMYFg2RgqF3QmWjTyBUgRZcF7LoKyufHtS4dIudM6k1aHBpSaDrhw==
- dependencies:
- tslib "^1.9.3"
-
-"@snyk/cloud-config-parser@^1.9.2":
- version "1.9.2"
- resolved "https://registry.yarnpkg.com/@snyk/cloud-config-parser/-/cloud-config-parser-1.9.2.tgz#e6c8e575db8527b33cf1ba766f86e1b3414cf6e1"
- integrity sha512-m8Y2+3l4fxj96QMrTfiCEaXgCpDkCkJIX/5wv0V0RHuxpUiyh+KxC2yJ8Su4wybBj6v6hB9hB7h5/L+Gy4V4PA==
- dependencies:
- esprima "^4.0.1"
- tslib "^1.10.0"
- yaml-js "^0.3.0"
-
-"@snyk/cocoapods-lockfile-parser@3.6.2":
- version "3.6.2"
- resolved "https://registry.yarnpkg.com/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.6.2.tgz#803ae9466f408c48ba7c5a8ec51b9dbac6f633b3"
- integrity sha512-ca2JKOnSRzYHJkhOB9gYmdRZHmd02b/uBd/S0D5W+L9nIMS7sUBV5jfhKwVgrYPIpVNIc0XCI9rxK4TfkQRpiA==
- dependencies:
- "@snyk/dep-graph" "^1.23.1"
- "@types/js-yaml" "^3.12.1"
- js-yaml "^3.13.1"
- tslib "^1.10.0"
-
-"@snyk/code-client@3.4.1":
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/@snyk/code-client/-/code-client-3.4.1.tgz#b9d025897cd586e0aef903162ac0407d0bffc3cd"
- integrity sha512-XJ7tUdX1iQyzN/BmHac7p+Oyw1SyTcqSkCNExwBJxyQdlnUAKK6QKIWLXS81tTpZ79FgCdT+0fdS0AjsyS99eA==
- dependencies:
- "@deepcode/dcignore" "^1.0.2"
- "@snyk/fast-glob" "^3.2.6-patch"
- "@types/flat-cache" "^2.0.0"
- "@types/lodash.chunk" "^4.2.6"
- "@types/lodash.omit" "^4.5.6"
- "@types/lodash.union" "^4.6.6"
- "@types/micromatch" "^4.0.1"
- "@types/sarif" "^2.1.3"
- "@types/uuid" "^8.3.0"
- axios "^0.21.1"
- ignore "^5.1.8"
- lodash.chunk "^4.2.0"
- lodash.omit "^4.5.0"
- lodash.union "^4.6.0"
- micromatch "^4.0.2"
- queue "^6.0.1"
- uuid "^8.3.2"
-
-"@snyk/composer-lockfile-parser@^1.4.1":
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.1.tgz#2f7c93ad367520322b16d9490a208fec08445e0e"
- integrity sha512-wNANv235j95NFsQuODIXCiQZ9kcyg9fz92Kg1zoGvaP3kN/ma7fgCnvQL/dyml6iouQJR5aZovjhrrfEFoKtiQ==
- dependencies:
- lodash.findkey "^4.6.0"
- lodash.get "^4.4.2"
- lodash.invert "^4.3.0"
- lodash.isempty "^4.4.0"
-
-"@snyk/dep-graph@^1.19.3", "@snyk/dep-graph@^1.21.0", "@snyk/dep-graph@^1.23.0", "@snyk/dep-graph@^1.23.1", "@snyk/dep-graph@^1.27.1", "@snyk/dep-graph@^1.28.0":
- version "1.28.0"
- resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.28.0.tgz#d68c0576cb3562c6e819ca8a8c7ac29ee11d9776"
- integrity sha512-Oup9nAvb558jdNvbZah/vaBtOtCcizkdeS+OBQeBIqIffyer4mc4juSn4b1SFjCpu7AG7piio8Lj8k1B9ps6Tg==
- dependencies:
- event-loop-spinner "^2.1.0"
- lodash.clone "^4.5.0"
- lodash.constant "^3.0.0"
- lodash.filter "^4.6.0"
- lodash.foreach "^4.5.0"
- lodash.isempty "^4.4.0"
- lodash.isequal "^4.5.0"
- lodash.isfunction "^3.0.9"
- lodash.isundefined "^3.0.1"
- lodash.keys "^4.2.0"
- lodash.map "^4.6.0"
- lodash.reduce "^4.6.0"
- lodash.size "^4.2.0"
- lodash.transform "^4.6.0"
- lodash.union "^4.6.0"
- lodash.values "^4.3.0"
- object-hash "^2.0.3"
- semver "^7.0.0"
- tslib "^1.13.0"
-
-"@snyk/docker-registry-v2-client@1.13.9":
- version "1.13.9"
- resolved "https://registry.yarnpkg.com/@snyk/docker-registry-v2-client/-/docker-registry-v2-client-1.13.9.tgz#54c2e3071de58fc6fc12c5fef5eaeae174ecda12"
- integrity sha512-DIFLEhr8m1GrAwsLGInJmpcQMacjuhf3jcbpQTR+LeMvZA9IuKq+B7kqw2O2FzMiHMZmUb5z+tV+BR7+IUHkFQ==
- dependencies:
- needle "^2.5.0"
- parse-link-header "^1.0.1"
- tslib "^1.10.0"
-
-"@snyk/fast-glob@^3.2.6-patch":
- version "3.2.6-patch"
- resolved "https://registry.yarnpkg.com/@snyk/fast-glob/-/fast-glob-3.2.6-patch.tgz#a0866bedb17f95255e4050dad08daeaff0f4caa8"
- integrity sha512-E/Pfdze/WFfxwyuTFcfhQN1SwyUsc43yuCoW63RVBCaxTD6OzhVD2Pvc/Sy7BjiWUfmelzyKkIBpoow8zZX7Zg==
- dependencies:
- "@nodelib/fs.stat" "^2.0.2"
- "@nodelib/fs.walk" "^1.2.3"
- "@snyk/glob-parent" "^5.1.2-patch.1"
- merge2 "^1.3.0"
- micromatch "^4.0.2"
- picomatch "^2.2.1"
-
-"@snyk/fix@1.554.0":
- version "1.554.0"
- resolved "https://registry.yarnpkg.com/@snyk/fix/-/fix-1.554.0.tgz#7ae786882e0ffea5e7f10d0b41e3d593b65555c4"
- integrity sha512-q2eRVStgspPeI2wZ2EQGLpiWZMRg7o+4tsCk6m/kHZgQGDN4Bb7L3xslFW3OgF0+ZksYSaHl2cW2HmGiLRaYcA==
- dependencies:
- "@snyk/dep-graph" "^1.21.0"
- chalk "4.1.0"
- debug "^4.3.1"
- ora "5.3.0"
- p-map "^4.0.0"
- strip-ansi "6.0.0"
-
-"@snyk/gemfile@1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.2.0.tgz#919857944973cce74c650e5428aaf11bcd5c0457"
- integrity sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA==
-
-"@snyk/glob-parent@^5.1.2-patch.1":
- version "5.1.2-patch.1"
- resolved "https://registry.yarnpkg.com/@snyk/glob-parent/-/glob-parent-5.1.2-patch.1.tgz#87733b4ab282043fa7915200bc94cb391df6d44f"
- integrity sha512-OkUPdHgxIWKAAzceG1nraNA0kgI+eS0I9wph8tll9UL0slD2mIWSj4mAqroGovaEXm8nHedoUfuDRGEb6wnzCQ==
- dependencies:
- is-glob "^4.0.1"
-
-"@snyk/graphlib@2.1.9-patch.3", "@snyk/graphlib@^2.1.9-patch.3":
- version "2.1.9-patch.3"
- resolved "https://registry.yarnpkg.com/@snyk/graphlib/-/graphlib-2.1.9-patch.3.tgz#b8edb2335af1978db7f3cb1f28f5d562960acf23"
- integrity sha512-bBY9b9ulfLj0v2Eer0yFYa3syVeIxVKl2EpxSrsVeT4mjA0CltZyHsF0JjoaGXP27nItTdJS5uVsj1NA+3aE+Q==
- dependencies:
- lodash.clone "^4.5.0"
- lodash.constant "^3.0.0"
- lodash.filter "^4.6.0"
- lodash.foreach "^4.5.0"
- lodash.has "^4.5.2"
- lodash.isempty "^4.4.0"
- lodash.isfunction "^3.0.9"
- lodash.isundefined "^3.0.1"
- lodash.keys "^4.2.0"
- lodash.map "^4.6.0"
- lodash.reduce "^4.6.0"
- lodash.size "^4.2.0"
- lodash.transform "^4.6.0"
- lodash.union "^4.6.0"
- lodash.values "^4.3.0"
-
-"@snyk/inquirer@^7.3.3-patch":
- version "7.3.3-patch"
- resolved "https://registry.yarnpkg.com/@snyk/inquirer/-/inquirer-7.3.3-patch.tgz#ef84d531724c53b755e8dd454e1a3c2ccdcfc0bf"
- integrity sha512-aWiQSOacH2lOpJ1ard9ErABcH4tdJogdr+mg1U67iZJOPO9n2gFgAwz1TQJDyPkv4/A5mh4hT2rg03Uq+KBn2Q==
- dependencies:
- ansi-escapes "^4.2.1"
- chalk "^4.1.0"
- cli-cursor "^3.1.0"
- cli-width "^3.0.0"
- external-editor "^3.0.3"
- figures "^3.0.0"
- lodash.assign "^4.2.0"
- lodash.assignin "^4.2.0"
- lodash.clone "^4.5.0"
- lodash.defaults "^4.2.0"
- lodash.filter "^4.6.0"
- lodash.find "^4.6.0"
- lodash.findindex "^4.6.0"
- lodash.flatten "^4.4.0"
- lodash.isboolean "^3.0.3"
- lodash.isfunction "^3.0.9"
- lodash.isnumber "^3.0.3"
- lodash.isplainobject "^4.0.6"
- lodash.isstring "^4.0.1"
- lodash.last "^3.0.0"
- lodash.map "^4.6.0"
- lodash.omit "^4.5.0"
- lodash.set "^4.3.2"
- lodash.sum "^4.0.2"
- lodash.uniq "^4.5.0"
- mute-stream "0.0.8"
- run-async "^2.4.0"
- rxjs "^6.6.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
- through "^2.3.6"
-
-"@snyk/java-call-graph-builder@1.19.1":
- version "1.19.1"
- resolved "https://registry.yarnpkg.com/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.19.1.tgz#1d579d782df3bb5f9d5171cc35180596cd90aa8b"
- integrity sha512-bxjHef5Qm3pNc+BrFlxMudmSSbOjA395ZqBddc+dvsFHoHeyNbiY56Y1JSGUlTgjRM+PKNPBiCuELTSMaROeZg==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch.3"
- ci-info "^2.0.0"
- debug "^4.1.1"
- glob "^7.1.6"
- jszip "^3.2.2"
- needle "^2.3.3"
- progress "^2.0.3"
- snyk-config "^4.0.0-rc.2"
- source-map-support "^0.5.7"
- temp-dir "^2.0.0"
- tmp "^0.2.1"
- tslib "^1.9.3"
- xml-js "^1.6.11"
-
-"@snyk/java-call-graph-builder@1.20.0":
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.20.0.tgz#ffca734cf7ce276a69277963149358190eaac3e5"
- integrity sha512-NX8bpIu7oG5cuSSm6WvtxqcCuJs2gRjtKhtuSeF1p5TYXyESs3FXQ0nHjfY90LiyTTc+PW/UBq6SKbBA6bCBww==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch.3"
- ci-info "^2.0.0"
- debug "^4.1.1"
- glob "^7.1.6"
- jszip "^3.2.2"
- needle "^2.3.3"
- progress "^2.0.3"
- snyk-config "^4.0.0-rc.2"
- source-map-support "^0.5.7"
- temp-dir "^2.0.0"
- tmp "^0.2.1"
- tslib "^1.9.3"
- xml-js "^1.6.11"
-
-"@snyk/mix-parser@^1.1.1":
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/@snyk/mix-parser/-/mix-parser-1.3.2.tgz#930de1d9c3a91e20660751f78c3e6f6a88ac5b2b"
- integrity sha512-0Aq9vcgmjH0d9Gk5q0k6l4ZOvSHPf6/BCQGDVOpKp0hwOkXWnpDOLLPxL+uBCktuH9zTYQFB0aTk91kQImZqmA==
- dependencies:
- "@snyk/dep-graph" "^1.28.0"
- tslib "^2.0.0"
-
-"@snyk/rpm-parser@^2.0.0":
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/@snyk/rpm-parser/-/rpm-parser-2.2.1.tgz#b61ccf5478684b203576bd2be68de434ccbb0069"
- integrity sha512-OAON0bPf3c5fgM/GK9DX0aZErB6SnuRyYlPH0rqI1TXGsKrYnVELhaE6ctNbEfPTQuY9r6q0vM+UYDaFM/YliA==
- dependencies:
- event-loop-spinner "^2.0.0"
-
-"@snyk/snyk-cocoapods-plugin@2.5.2":
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.5.2.tgz#cd724fcd637cb3af76187bf7254819b6079489f6"
- integrity sha512-WHhnwyoGOhjFOjBXqUfszD84SErrtjHjium/4xFbqKpEE+yuwxs8OwV/S29BtxhYiGtjpD1azv5QtH30VUMl0A==
- dependencies:
- "@snyk/cli-interface" "^2.11.0"
- "@snyk/cocoapods-lockfile-parser" "3.6.2"
- "@snyk/dep-graph" "^1.23.1"
- source-map-support "^0.5.7"
- tslib "^2.0.0"
-
-"@snyk/snyk-docker-pull@3.2.3":
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/@snyk/snyk-docker-pull/-/snyk-docker-pull-3.2.3.tgz#9743ea624098c7abd0f95c438c76067530494f4b"
- integrity sha512-hiFiSmWGLc2tOI7FfgIhVdFzO2f69im8O6p3OV4xEZ/Ss1l58vwtqudItoswsk7wj/azRlgfBW8wGu2MjoudQg==
- dependencies:
- "@snyk/docker-registry-v2-client" "1.13.9"
- child-process "^1.0.2"
- tar-stream "^2.1.2"
- tmp "^0.1.0"
-
-"@snyk/snyk-hex-plugin@1.1.4":
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/@snyk/snyk-hex-plugin/-/snyk-hex-plugin-1.1.4.tgz#4a5b1684cecc1a557ec1a9f5f8646683ae89f0da"
- integrity sha512-kLfFGckSmyKe667UGPyWzR/H7/Trkt4fD8O/ktElOx1zWgmivpLm0Symb4RCfEmz9irWv+N6zIKRrfSNdytcPQ==
- dependencies:
- "@snyk/dep-graph" "^1.28.0"
- "@snyk/mix-parser" "^1.1.1"
- debug "^4.3.1"
- tmp "^0.0.33"
- tslib "^2.0.0"
- upath "2.0.1"
-
-"@szmarczak/http-timer@^1.1.2":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
- integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
- dependencies:
- defer-to-connect "^1.0.1"
-
-"@szmarczak/http-timer@^4.0.5":
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152"
- integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==
- dependencies:
- defer-to-connect "^2.0.0"
-
-"@types/braces@*":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/braces/-/braces-3.0.0.tgz#7da1c0d44ff1c7eb660a36ec078ea61ba7eb42cb"
- integrity sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==
-
-"@types/cacheable-request@^6.0.1":
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
- integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==
- dependencies:
- "@types/http-cache-semantics" "*"
- "@types/keyv" "*"
- "@types/node" "*"
- "@types/responselike" "*"
-
-"@types/debug@^4.1.4":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
- integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
-
-"@types/emscripten@^1.38.0":
- version "1.39.4"
- resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.4.tgz#d61990c0cee72c4e475de737a140b51fe925a2c8"
- integrity sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==
-
-"@types/flat-cache@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@types/flat-cache/-/flat-cache-2.0.0.tgz#64e5d3b426c392b603a208a55bdcc7d920ce6e57"
- integrity sha512-fHeEsm9hvmZ+QHpw6Fkvf19KIhuqnYLU6vtWLjd5BsMd/qVi7iTkMioDZl0mQmfNRA1A6NwvhrSRNr9hGYZGww==
-
-"@types/graphlib@^2":
- version "2.1.7"
- resolved "https://registry.yarnpkg.com/@types/graphlib/-/graphlib-2.1.7.tgz#e6a47a4f43511f5bad30058a669ce5ce93bfd823"
- integrity sha512-K7T1n6U2HbTYu+SFHlBjz/RH74OA2D/zF1qlzn8uXbvB4uRg7knOM85ugS2bbXI1TXMh7rLqk4OVRwIwEBaixg==
-
-"@types/http-cache-semantics@*":
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
- integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==
-
-"@types/js-yaml@^3.12.1":
- version "3.12.2"
- resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.2.tgz#a35a1809c33a68200fb6403d1ad708363c56470a"
- integrity sha512-0CFu/g4mDSNkodVwWijdlr8jH7RoplRWNgovjFLEZeT+QEbbZXjBmCe3HwaWheAlCbHwomTwzZoSedeOycABug==
-
-"@types/keyv@*":
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
- integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==
- dependencies:
- "@types/node" "*"
-
-"@types/lodash.chunk@^4.2.6":
- version "4.2.6"
- resolved "https://registry.yarnpkg.com/@types/lodash.chunk/-/lodash.chunk-4.2.6.tgz#9d35f05360b0298715d7f3d9efb34dd4f77e5d2a"
- integrity sha512-SPlusB7jxXyGcTXYcUdWr7WmhArO/rmTq54VN88iKMxGUhyg79I4Q8n4riGn3kjaTjOJrVlHhxgX/d7woak5BQ==
- dependencies:
- "@types/lodash" "*"
-
-"@types/lodash.omit@^4.5.6":
- version "4.5.6"
- resolved "https://registry.yarnpkg.com/@types/lodash.omit/-/lodash.omit-4.5.6.tgz#f2a9518259e481a48ff7ec423420fa8fd58933e2"
- integrity sha512-KXPpOSNX2h0DAG2w7ajpk7TXvWF28ZHs5nJhOJyP0BQHkehgr948RVsToItMme6oi0XJkp19CbuNXkIX8FiBlQ==
- dependencies:
- "@types/lodash" "*"
-
-"@types/lodash.union@^4.6.6":
- version "4.6.6"
- resolved "https://registry.yarnpkg.com/@types/lodash.union/-/lodash.union-4.6.6.tgz#2f77f2088326ed147819e9e384182b99aae8d4b0"
- integrity sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw==
- dependencies:
- "@types/lodash" "*"
-
-"@types/lodash@*":
- version "4.14.168"
- resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
- integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
-
-"@types/micromatch@^4.0.1":
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-4.0.1.tgz#9381449dd659fc3823fd2a4190ceacc985083bc7"
- integrity sha512-my6fLBvpY70KattTNzYOK6KU1oR1+UCz9ug/JbcF5UrEmeCt9P7DV2t7L8+t18mMPINqGQCE4O8PLOPbI84gxw==
- dependencies:
- "@types/braces" "*"
-
-"@types/node@*":
- version "13.5.3"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-13.5.3.tgz#37f1f539b7535b9fb4ef77d59db1847a837b7f17"
- integrity sha512-ZPnWX9PW992w6DUsz3JIXHaSb5v7qmKCVzC3km6SxcDGxk7zmLfYaCJTbktIa5NeywJkkZDhGldKqDIvC5DRrA==
-
-"@types/node@^13.7.0":
- version "13.13.50"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.50.tgz#bc8ebf70c392a98ffdba7aab9b46989ea96c1c62"
- integrity sha512-y7kkh+hX/0jZNxMyBR/6asG0QMSaPSzgeVK63dhWHl4QAXCQB8lExXmzLL6SzmOgKHydtawpMnNhlDbv7DXPEA==
-
-"@types/responselike@*", "@types/responselike@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
- integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
- dependencies:
- "@types/node" "*"
-
-"@types/sarif@^2.1.3":
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/@types/sarif/-/sarif-2.1.3.tgz#1f9c16033f1461536ac014284920350109614c02"
- integrity sha512-zf+EoIplTkQW2TV2mwtJtlI0g540Z3Rs9tX9JqRAtyjnDCqkP+eMTgWCj3PGNbQpi+WXAjvC3Ou/dvvX2sLK4w==
-
-"@types/semver@^7.1.0":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb"
- integrity sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==
-
-"@types/treeify@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@types/treeify/-/treeify-1.0.0.tgz#f04743cb91fc38254e8585d692bd92503782011c"
- integrity sha512-ONpcZAEYlbPx4EtJwfTyCDQJGUpKf4sEcuySdCVjK5Fj/3vHp5HII1fqa1/+qrsLnpYELCQTfVW/awsGJePoIg==
-
-"@types/uuid@^8.3.0":
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
- integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
-
-"@yarnpkg/core@^2.4.0":
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/core/-/core-2.4.0.tgz#b5d8cc7ee2ddb022816c7afa3f83c3ee3d317c80"
- integrity sha512-FYjcPNTfDfMKLFafQPt49EY28jnYC82Z2S7oMwLPUh144BL8v8YXzb4aCnFyi5nFC5h2kcrJfZh7+Pm/qvCqGw==
- dependencies:
- "@arcanis/slice-ansi" "^1.0.2"
- "@types/semver" "^7.1.0"
- "@types/treeify" "^1.0.0"
- "@yarnpkg/fslib" "^2.4.0"
- "@yarnpkg/json-proxy" "^2.1.0"
- "@yarnpkg/libzip" "^2.2.1"
- "@yarnpkg/parsers" "^2.3.0"
- "@yarnpkg/pnp" "^2.3.2"
- "@yarnpkg/shell" "^2.4.1"
- binjumper "^0.1.4"
- camelcase "^5.3.1"
- chalk "^3.0.0"
- ci-info "^2.0.0"
- clipanion "^2.6.2"
- cross-spawn "7.0.3"
- diff "^4.0.1"
- globby "^11.0.1"
- got "^11.7.0"
- json-file-plus "^3.3.1"
- lodash "^4.17.15"
- micromatch "^4.0.2"
- mkdirp "^0.5.1"
- p-limit "^2.2.0"
- pluralize "^7.0.0"
- pretty-bytes "^5.1.0"
- semver "^7.1.2"
- stream-to-promise "^2.2.0"
- tar-stream "^2.0.1"
- treeify "^1.1.0"
- tslib "^1.13.0"
- tunnel "^0.0.6"
-
-"@yarnpkg/fslib@^2.1.0", "@yarnpkg/fslib@^2.4.0":
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/fslib/-/fslib-2.4.0.tgz#a265b737cd089ef293ad964e06c143f5efd411a9"
- integrity sha512-CwffYY9owtl3uImNOn1K4jl5iIb/L16a9UZ9Q3lkBARk6tlUsPrNFX00eoUlFcLn49TTfd3zdN6higloGCyncw==
- dependencies:
- "@yarnpkg/libzip" "^2.2.1"
- tslib "^1.13.0"
-
-"@yarnpkg/json-proxy@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/json-proxy/-/json-proxy-2.1.0.tgz#362a161678cd7dda74b47b4fc848a2f1730d16cd"
- integrity sha512-rOgCg2DkyviLgr80mUMTt9vzdf5RGOujQB26yPiXjlz4WNePLBshKlTNG9rKSoKQSOYEQcw6cUmosfOKDatrCw==
- dependencies:
- "@yarnpkg/fslib" "^2.1.0"
- tslib "^1.13.0"
-
-"@yarnpkg/libzip@^2.2.1":
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/@yarnpkg/libzip/-/libzip-2.2.1.tgz#61c9b8b2499ee6bd9c4fcbf8248f68e07bd89948"
- integrity sha512-AYDJXrkzayoDd3ZlVgFJ+LyDX+Zj/cki3vxIpcYxejtgkl3aquVWOxlC0DD9WboBWsJFIP1MjrUbchLyh++/7A==
- dependencies:
- "@types/emscripten" "^1.38.0"
- tslib "^1.13.0"
-
-"@yarnpkg/lockfile@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
- integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
-
-"@yarnpkg/parsers@^2.3.0":
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-2.3.0.tgz#7b9564c6df02f4921d5cfe8287c4b648e93ea84b"
- integrity sha512-qgz0QUgOvnhtF92kaluIhIIKBUHlYlHUBQxqh5v9+sxEQvUeF6G6PKiFlzo3E6O99XwvNEGpVu1xZPoSGyGscQ==
- dependencies:
- js-yaml "^3.10.0"
- tslib "^1.13.0"
-
-"@yarnpkg/pnp@^2.3.2":
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/@yarnpkg/pnp/-/pnp-2.3.2.tgz#9a052a06bf09c9f0b7c31e0867a7e725cb6401ed"
- integrity sha512-JdwHu1WBCISqJEhIwx6Hbpe8MYsYbkGMxoxolkDiAeJ9IGEe08mQcbX1YmUDV1ozSWlm9JZE90nMylcDsXRFpA==
- dependencies:
- "@types/node" "^13.7.0"
- "@yarnpkg/fslib" "^2.4.0"
- tslib "^1.13.0"
-
-"@yarnpkg/shell@^2.4.1":
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/@yarnpkg/shell/-/shell-2.4.1.tgz#abc557f8924987c9c382703e897433a82780265d"
- integrity sha512-oNNJkH8ZI5uwu0dMkJf737yMSY1WXn9gp55DqSA5wAOhKvV5DJTXFETxkVgBQhO6Bow9tMGSpvowTMD/oAW/9g==
- dependencies:
- "@yarnpkg/fslib" "^2.4.0"
- "@yarnpkg/parsers" "^2.3.0"
- clipanion "^2.6.2"
- cross-spawn "7.0.3"
- fast-glob "^3.2.2"
- micromatch "^4.0.2"
- stream-buffers "^3.0.2"
- tslib "^1.13.0"
-
-abbrev@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
- integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
-
-aggregate-error@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
- integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
- dependencies:
- clean-stack "^2.0.0"
- indent-string "^4.0.0"
-
-ansi-align@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
- integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==
- dependencies:
- string-width "^3.0.0"
-
-ansi-escapes@3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
- integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
-
-ansi-escapes@^4.2.1:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
- integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
- dependencies:
- type-fest "^0.21.3"
-
-ansi-regex@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
- integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
-
-ansi-regex@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
- integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
-
-ansi-styles@^3.2.0, ansi-styles@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
- integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
- dependencies:
- color-convert "^1.9.0"
-
-ansi-styles@^4.1.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
- integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
- dependencies:
- color-convert "^2.0.1"
-
-ansicolors@^0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
- integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=
-
-any-promise@^1.1.0, any-promise@~1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
- integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
-
-archy@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
- integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
-
-argparse@^1.0.7:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
- integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
- dependencies:
- sprintf-js "~1.0.2"
-
-array-union@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
- integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-
-asap@~2.0.3:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
- integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
-
-asn1@~0.2.0:
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
- integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
- dependencies:
- safer-buffer "~2.1.0"
-
-async@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
- integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
-
-axios@^0.21.1:
- version "0.21.1"
- resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
- integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
- dependencies:
- follow-redirects "^1.10.0"
-
-balanced-match@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
- integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
-
-base64-js@^1.3.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
- integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
-
-bcrypt-pbkdf@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
- integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
- dependencies:
- tweetnacl "^0.14.3"
-
-binjumper@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/binjumper/-/binjumper-0.1.4.tgz#4acc0566832714bd6508af6d666bd9e5e21fc7f8"
- integrity sha512-Gdxhj+U295tIM6cO4bJO1jsvSjBVHNpj2o/OwW7pqDEtaqF6KdOxjtbo93jMMKAkP7+u09+bV8DhSqjIv4qR3w==
-
-bl@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f"
- integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==
- dependencies:
- readable-stream "^3.0.1"
-
-bl@^4.0.3:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
- integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
- dependencies:
- buffer "^5.5.0"
- inherits "^2.0.4"
- readable-stream "^3.4.0"
-
-boolean@^3.0.1:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.3.tgz#0fee0c9813b66bef25a8a6a904bb46736d05f024"
- integrity sha512-EqrTKXQX6Z3A2nRmMEIlAIfjQOgFnVO2nqZGpbcsPnYGWBwpFqzlrozU1dy+S2iqfYDLh26ef4KrgTxu9xQrxA==
-
-boxen@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
- integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==
- dependencies:
- ansi-align "^3.0.0"
- camelcase "^5.3.1"
- chalk "^3.0.0"
- cli-boxes "^2.2.0"
- string-width "^4.1.0"
- term-size "^2.1.0"
- type-fest "^0.8.1"
- widest-line "^3.1.0"
-
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
-braces@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
-browserify-zlib@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d"
- integrity sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=
- dependencies:
- pako "~0.2.0"
-
-buffer-from@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
- integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
-
-buffer@^5.5.0:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
- integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
- dependencies:
- base64-js "^1.3.1"
- ieee754 "^1.1.13"
-
-cacheable-lookup@^5.0.3:
- version "5.0.4"
- resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
- integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
-
-cacheable-request@^6.0.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
- integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
- dependencies:
- clone-response "^1.0.2"
- get-stream "^5.1.0"
- http-cache-semantics "^4.0.0"
- keyv "^3.0.0"
- lowercase-keys "^2.0.0"
- normalize-url "^4.1.0"
- responselike "^1.0.2"
-
-cacheable-request@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58"
- integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==
- dependencies:
- clone-response "^1.0.2"
- get-stream "^5.1.0"
- http-cache-semantics "^4.0.0"
- keyv "^4.0.0"
- lowercase-keys "^2.0.0"
- normalize-url "^4.1.0"
- responselike "^2.0.0"
-
-call-bind@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
- integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
- dependencies:
- function-bind "^1.1.1"
- get-intrinsic "^1.0.2"
-
-camelcase@^5.3.1:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
- integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-
-chalk@4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
- integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
-chalk@^2.4.2:
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
- integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
- dependencies:
- ansi-styles "^3.2.1"
- escape-string-regexp "^1.0.5"
- supports-color "^5.3.0"
-
-chalk@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
- integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
-chalk@^4.1.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
- integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
-chardet@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
- integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
-
-child-process@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/child-process/-/child-process-1.0.2.tgz#98974dc7ed1ee4c6229f8e305fa7313a6885a7f2"
- integrity sha1-mJdNx+0e5MYin44wX6cxOmiFp/I=
-
-chownr@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
- integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
-
-ci-info@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
- integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
-
-clean-stack@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
- integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-
-cli-boxes@^2.2.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
- integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
-
-cli-cursor@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
- integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
- dependencies:
- restore-cursor "^3.1.0"
-
-cli-spinner@0.2.10:
- version "0.2.10"
- resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"
- integrity sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==
-
-cli-spinners@^2.5.0:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939"
- integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==
-
-cli-width@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
- integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
-
-clipanion@^2.6.2:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-2.6.2.tgz#820e7440812052442455b248f927b187ed732f71"
- integrity sha512-0tOHJNMF9+4R3qcbBL+4IxLErpaYSYvzs10aXuECDbZdJOuJHdagJMAqvLdeaUQTI/o2uSCDRpet6ywDiKOAYw==
-
-clone-response@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
- integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
- dependencies:
- mimic-response "^1.0.0"
-
-clone@^1.0.2:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
- integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
-
-color-convert@^1.9.0:
- version "1.9.3"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
- integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
- dependencies:
- color-name "1.1.3"
-
-color-convert@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
- integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
- dependencies:
- color-name "~1.1.4"
-
-color-name@1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
- integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
-
-color-name@~1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
- integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
- integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-
-configstore@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
- integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==
- dependencies:
- dot-prop "^5.2.0"
- graceful-fs "^4.1.2"
- make-dir "^3.0.0"
- unique-string "^2.0.0"
- write-file-atomic "^3.0.0"
- xdg-basedir "^4.0.0"
-
-core-js@^3.6.5:
- version "3.11.0"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.11.0.tgz#05dac6aa70c0a4ad842261f8957b961d36eb8926"
- integrity sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw==
-
-core-util-is@~1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
- integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
-
-cross-spawn@7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
- integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
-cross-spawn@^6.0.0:
- version "6.0.5"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
- integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
- dependencies:
- nice-try "^1.0.4"
- path-key "^2.0.1"
- semver "^5.5.0"
- shebang-command "^1.2.0"
- which "^1.2.9"
-
-crypto-random-string@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
- integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
-
-debug@^3.1.0, debug@^3.2.6:
- version "3.2.6"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
- integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
- dependencies:
- ms "^2.1.1"
-
-debug@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
- integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
- dependencies:
- ms "^2.1.1"
-
-debug@^4.2.0, debug@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
- integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
- dependencies:
- ms "2.1.2"
-
-decompress-response@^3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
- integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
- dependencies:
- mimic-response "^1.0.0"
-
-decompress-response@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
- integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
- dependencies:
- mimic-response "^3.1.0"
-
-deep-extend@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
- integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
-
-defaults@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
- integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
- dependencies:
- clone "^1.0.2"
-
-defer-to-connect@^1.0.1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
- integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
-
-defer-to-connect@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
- integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
-
-define-properties@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
- integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
- dependencies:
- object-keys "^1.0.12"
-
-detect-node@^2.0.4:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79"
- integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==
-
-diff@^4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
- integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
-
-dir-glob@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
- integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
- dependencies:
- path-type "^4.0.0"
-
-docker-modem@2.1.3:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-2.1.3.tgz#15432225f63db02eb5de4bb9a621b7293e5f264d"
- integrity sha512-cwaRptBmYZwu/FyhGcqBm2MzXA77W2/E6eVkpOZVDk6PkI9Bjj84xPrXiHMA+OWjzNy+DFjgKh8Q+1hMR7/OHg==
- dependencies:
- debug "^4.1.1"
- readable-stream "^3.5.0"
- split-ca "^1.0.1"
- ssh2 "^0.8.7"
-
-dockerfile-ast@0.2.0:
+base64-arraybuffer@^0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/dockerfile-ast/-/dockerfile-ast-0.2.0.tgz#13cc4a6fe3aea30a4104622b30f49a0fe3a5c038"
- integrity sha512-iQyp12k1A4tF3sEfLAq2wfFPKdpoiGTJeuiu2Y1bdEqIZu0DfSSL2zm0fk7a/UHeQkngnYaRRGuON+C+2LO1Fw==
- dependencies:
- vscode-languageserver-types "^3.16.0"
+ resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
+ integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==
-dot-prop@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
- integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
- dependencies:
- is-obj "^2.0.0"
-
-dotnet-deps-parser@5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/dotnet-deps-parser/-/dotnet-deps-parser-5.0.0.tgz#5115c442cbefea59e4fb9f9ed8fa4863a0f3186d"
- integrity sha512-1l9K4UnQQHSfKgeHeLrxnB53AidCZqPyf9dkRL4/fZl8//NPiiDD43zHtgylw8DHlO7gvM8+O5a0UPHesNYZKw==
- dependencies:
- lodash.isempty "^4.4.0"
- lodash.set "^4.3.2"
- lodash.uniq "^4.5.0"
- source-map-support "^0.5.7"
- tslib "^1.10.0"
- xml2js "0.4.23"
-
-duplexer3@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
- integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
-
-duplexify@^3.5.0, duplexify@^3.6.0:
- version "3.7.1"
- resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
- integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
- dependencies:
- end-of-stream "^1.0.0"
- inherits "^2.0.1"
- readable-stream "^2.0.0"
- stream-shift "^1.0.0"
-
-elfy@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/elfy/-/elfy-1.0.0.tgz#7a1c86af7d41e0a568cbb4a3fa5b685648d9efcd"
- integrity sha512-4Kp3AA94jC085IJox+qnvrZ3PudqTi4gQNvIoTZfJJ9IqkRuCoqP60vCVYlIg00c5aYusi5Wjh2bf0cHYt+6gQ==
- dependencies:
- endian-reader "^0.3.0"
-
-email-validator@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed"
- integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==
-
-emoji-regex@^7.0.1:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
- integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
-
-emoji-regex@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
- integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-
-end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
- integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
- dependencies:
- once "^1.4.0"
-
-end-of-stream@~1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07"
- integrity sha1-6TUyWLqpEIll78QcsO+K3i88+wc=
- dependencies:
- once "~1.3.0"
-
-endian-reader@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/endian-reader/-/endian-reader-0.3.0.tgz#84eca436b80aed0d0639c47291338b932efe50a0"
- integrity sha1-hOykNrgK7Q0GOcRykTOLky7+UKA=
-
-es6-error@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
- integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
-
-escape-goat@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
- integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
-
-escape-string-regexp@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
- integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
-
-escape-string-regexp@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
- integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-
-esprima@^4.0.0, esprima@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
- integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-
-event-loop-spinner@^2.0.0, event-loop-spinner@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/event-loop-spinner/-/event-loop-spinner-2.1.0.tgz#75f501d585105c6d57f174073b39af1b6b3a1567"
- integrity sha512-RJ10wL8/F9AlfBgRCvYctJIXSb9XkVmSCK3GGUvPD3dJrvTjDeDT0tmhcbEC6I2NEjNM9xD38HQJ4F/f/gb4VQ==
- dependencies:
- tslib "^2.1.0"
-
-execa@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
- integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
- dependencies:
- cross-spawn "^6.0.0"
- get-stream "^4.0.0"
- is-stream "^1.1.0"
- npm-run-path "^2.0.0"
- p-finally "^1.0.0"
- signal-exit "^3.0.0"
- strip-eof "^1.0.0"
-
-external-editor@^3.0.3:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
- integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
- dependencies:
- chardet "^0.7.0"
- iconv-lite "^0.4.24"
- tmp "^0.0.33"
-
-fast-glob@^3.1.1, fast-glob@^3.2.2:
- version "3.2.5"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
- integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
- dependencies:
- "@nodelib/fs.stat" "^2.0.2"
- "@nodelib/fs.walk" "^1.2.3"
- glob-parent "^5.1.0"
- merge2 "^1.3.0"
- micromatch "^4.0.2"
- picomatch "^2.2.1"
-
-fastq@^1.6.0:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
- integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==
- dependencies:
- reusify "^1.0.4"
-
-figures@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
- integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
- dependencies:
- escape-string-regexp "^1.0.5"
-
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
-follow-redirects@^1.10.0:
- version "1.14.0"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.0.tgz#f5d260f95c5f8c105894491feee5dc8993b402fe"
- integrity sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==
-
-fs-constants@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
- integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
-
-fs-minipass@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
- integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
- dependencies:
- minipass "^3.0.0"
-
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
- integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-
-function-bind@^1.1.1:
+css-line-break@1.1.1:
version "1.1.1"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
- integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
-
-get-intrinsic@^1.0.2:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
- integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
+ resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef"
+ integrity sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==
dependencies:
- function-bind "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.1"
+ base64-arraybuffer "^0.2.0"
-get-stream@^4.0.0, get-stream@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
- integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+html2canvas@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.1.4.tgz#53ae91cd26e9e9e623c56533cccb2e3f57c8124c"
+ integrity sha512-uHgQDwrXsRmFdnlOVFvHin9R7mdjjZvoBoXxicPR+NnucngkaLa5zIDW9fzMkiip0jSffyTyWedE8iVogYOeWg==
dependencies:
- pump "^3.0.0"
-
-get-stream@^5.1.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
- integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
- dependencies:
- pump "^3.0.0"
-
-glob-parent@^5.1.0:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
- integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
- dependencies:
- is-glob "^4.0.1"
-
-glob@^7.1.3, glob@^7.1.6:
- version "7.1.6"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
- integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-global-agent@^2.1.12:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc"
- integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==
- dependencies:
- boolean "^3.0.1"
- core-js "^3.6.5"
- es6-error "^4.1.1"
- matcher "^3.0.0"
- roarr "^2.15.3"
- semver "^7.3.2"
- serialize-error "^7.0.1"
-
-global-dirs@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d"
- integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==
- dependencies:
- ini "1.3.7"
-
-globalthis@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b"
- integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==
- dependencies:
- define-properties "^1.1.3"
-
-globby@^11.0.1:
- version "11.0.3"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb"
- integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==
- dependencies:
- array-union "^2.1.0"
- dir-glob "^3.0.1"
- fast-glob "^3.1.1"
- ignore "^5.1.4"
- merge2 "^1.3.0"
- slash "^3.0.0"
-
-got@11.4.0:
- version "11.4.0"
- resolved "https://registry.yarnpkg.com/got/-/got-11.4.0.tgz#1f0910310572af4efcc6890e1dacd7affb710b39"
- integrity sha512-XysJZuZNVpaQ37Oo2LV90MIkPeYITehyy1A0QzO1JwOXm8EWuEf9eeGk2XuHePvLEGnm9AVOI37bHwD6KYyBtg==
- dependencies:
- "@sindresorhus/is" "^2.1.1"
- "@szmarczak/http-timer" "^4.0.5"
- "@types/cacheable-request" "^6.0.1"
- "@types/responselike" "^1.0.0"
- cacheable-lookup "^5.0.3"
- cacheable-request "^7.0.1"
- decompress-response "^6.0.0"
- http2-wrapper "^1.0.0-beta.4.5"
- lowercase-keys "^2.0.0"
- p-cancelable "^2.0.0"
- responselike "^2.0.0"
-
-got@^11.7.0:
- version "11.8.2"
- resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599"
- integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==
- dependencies:
- "@sindresorhus/is" "^4.0.0"
- "@szmarczak/http-timer" "^4.0.5"
- "@types/cacheable-request" "^6.0.1"
- "@types/responselike" "^1.0.0"
- cacheable-lookup "^5.0.3"
- cacheable-request "^7.0.1"
- decompress-response "^6.0.0"
- http2-wrapper "^1.0.0-beta.5.2"
- lowercase-keys "^2.0.0"
- p-cancelable "^2.0.0"
- responselike "^2.0.0"
-
-got@^9.6.0:
- version "9.6.0"
- resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
- integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
- dependencies:
- "@sindresorhus/is" "^0.14.0"
- "@szmarczak/http-timer" "^1.1.2"
- cacheable-request "^6.0.0"
- decompress-response "^3.3.0"
- duplexer3 "^0.1.4"
- get-stream "^4.1.0"
- lowercase-keys "^1.0.1"
- mimic-response "^1.0.1"
- p-cancelable "^1.0.0"
- to-readable-stream "^1.0.0"
- url-parse-lax "^3.0.0"
-
-graceful-fs@^4.1.2:
- version "4.1.15"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
- integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
-
-grapheme-splitter@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
- integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
-
-gunzip-maybe@^1.4.2:
- version "1.4.2"
- resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac"
- integrity sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==
- dependencies:
- browserify-zlib "^0.1.4"
- is-deflate "^1.0.0"
- is-gzip "^1.0.0"
- peek-stream "^1.1.0"
- pumpify "^1.3.3"
- through2 "^2.0.3"
-
-has-flag@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-has-symbols@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
- integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
-
-has-yarn@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
- integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
-
-has@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
- integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
- dependencies:
- function-bind "^1.1.1"
-
-hosted-git-info@^3.0.4, hosted-git-info@^3.0.7:
- version "3.0.8"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d"
- integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==
- dependencies:
- lru-cache "^6.0.0"
-
-http-cache-semantics@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
- integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
-
-http2-wrapper@^1.0.0-beta.4.5, http2-wrapper@^1.0.0-beta.5.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
- integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
- dependencies:
- quick-lru "^5.1.1"
- resolve-alpn "^1.0.0"
-
-iconv-lite@^0.4.24, iconv-lite@^0.4.4:
- version "0.4.24"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
- integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
- dependencies:
- safer-buffer ">= 2.1.2 < 3"
-
-ieee754@^1.1.13:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
- integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
-
-ignore@^5.1.4, ignore@^5.1.8:
- version "5.1.8"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
- integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
-
-immediate@~3.0.5:
- version "3.0.6"
- resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
- integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
-
-import-lazy@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
- integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
-
-imurmurhash@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
- integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
-
-indent-string@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
- integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
-
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
- integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2, inherits@~2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
- integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
-
-inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
- integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
-ini@1.3.7, ini@~1.3.0:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
- integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
-
-is-callable@^1.1.5:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
- integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
-
-is-ci@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
- integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
- dependencies:
- ci-info "^2.0.0"
-
-is-deflate@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14"
- integrity sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ=
-
-is-docker@^2.0.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
- integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
-
-is-extglob@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
- integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
-
-is-fullwidth-code-point@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
- integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
-
-is-fullwidth-code-point@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
- integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-
-is-glob@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
- integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
- dependencies:
- is-extglob "^2.1.1"
-
-is-gzip@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83"
- integrity sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=
-
-is-installed-globally@^0.3.1:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
- integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
- dependencies:
- global-dirs "^2.0.1"
- is-path-inside "^3.0.1"
-
-is-interactive@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
- integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
-
-is-npm@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d"
- integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==
-
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
-is-obj@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
- integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
-
-is-path-inside@^3.0.1:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
- integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
-
-is-stream@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
- integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
-
-is-typedarray@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
- integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
-
-is-unicode-supported@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
- integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
-
-is-wsl@^2.1.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
- integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
- dependencies:
- is-docker "^2.0.0"
-
-is-yarn-global@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
- integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
-
-is@^3.2.1:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/is/-/is-3.3.0.tgz#61cff6dd3c4193db94a3d62582072b44e5645d79"
- integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==
-
-isarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
- integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
-
-isexe@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
- integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-
-js-yaml@^3.10.0:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
- integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-js-yaml@^3.13.1:
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
- integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-json-buffer@3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
- integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
-
-json-buffer@3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
- integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
-
-json-file-plus@^3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/json-file-plus/-/json-file-plus-3.3.1.tgz#f4363806b82819ff8803d83d539d6a9edd2a5258"
- integrity sha512-wo0q1UuiV5NsDPQDup1Km8IwEeqe+olr8tkWxeJq9Bjtcp7DZ0l+yrg28fSC3DEtrE311mhTZ54QGS6oiqnZEA==
- dependencies:
- is "^3.2.1"
- node.extend "^2.0.0"
- object.assign "^4.1.0"
- promiseback "^2.0.2"
- safer-buffer "^2.0.2"
-
-json-stringify-safe@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
- integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
-
-jszip@3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
- integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
- dependencies:
- lie "~3.3.0"
- pako "~1.0.2"
- readable-stream "~2.3.6"
- set-immediate-shim "~1.0.1"
-
-jszip@^3.2.2:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9"
- integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==
- dependencies:
- lie "~3.3.0"
- pako "~1.0.2"
- readable-stream "~2.3.6"
- set-immediate-shim "~1.0.1"
-
-keyv@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
- integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
- dependencies:
- json-buffer "3.0.0"
-
-keyv@^4.0.0:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254"
- integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==
- dependencies:
- json-buffer "3.0.1"
-
-latest-version@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
- integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
- dependencies:
- package-json "^6.3.0"
-
-lie@~3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
- integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
- dependencies:
- immediate "~3.0.5"
-
-lodash.assign@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
- integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
-
-lodash.assignin@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
- integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI=
-
-lodash.camelcase@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
- integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
-
-lodash.chunk@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc"
- integrity sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=
-
-lodash.clone@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
- integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=
-
-lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
- integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
-
-lodash.constant@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/lodash.constant/-/lodash.constant-3.0.0.tgz#bfe05cce7e515b3128925d6362138420bd624910"
- integrity sha1-v+Bczn5RWzEokl1jYhOEIL1iSRA=
-
-lodash.defaults@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
- integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
-
-lodash.endswith@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09"
- integrity sha1-/tWawXOO0+I27dcGTsRWRIs3vAk=
-
-lodash.filter@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
- integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=
-
-lodash.find@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
- integrity sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=
-
-lodash.findindex@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.findindex/-/lodash.findindex-4.6.0.tgz#a3245dee61fb9b6e0624b535125624bb69c11106"
- integrity sha1-oyRd7mH7m24GJLU1ElYku2nBEQY=
-
-lodash.findkey@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.findkey/-/lodash.findkey-4.6.0.tgz#83058e903b51cbb759d09ccf546dea3ea39c4718"
- integrity sha1-gwWOkDtRy7dZ0JzPVG3qPqOcRxg=
-
-lodash.flatmap@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e"
- integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=
-
-lodash.flatten@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
- integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
-
-lodash.flattendeep@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
- integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
-
-lodash.foreach@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
- integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=
-
-lodash.get@^4.4.2:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
- integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
-
-lodash.groupby@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1"
- integrity sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=
-
-lodash.has@^4.5.2:
- version "4.5.2"
- resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862"
- integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=
-
-lodash.invert@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.invert/-/lodash.invert-4.3.0.tgz#8ffe20d4b616f56bea8f1aa0c6ebd80dcf742aee"
- integrity sha1-j/4g1LYW9WvqjxqgxuvYDc90Ku4=
-
-lodash.isboolean@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
- integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
-
-lodash.isempty@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
- integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=
-
-lodash.isequal@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
- integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
-
-lodash.isfunction@^3.0.9:
- version "3.0.9"
- resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
- integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
-
-lodash.isnumber@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
- integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
-
-lodash.isobject@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
- integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=
-
-lodash.isplainobject@^4.0.6:
- version "4.0.6"
- resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
- integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
-
-lodash.isstring@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
- integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
-
-lodash.isundefined@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48"
- integrity sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=
-
-lodash.keys@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205"
- integrity sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=
-
-lodash.last@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/lodash.last/-/lodash.last-3.0.0.tgz#242f663112dd4c6e63728c60a3c909d1bdadbd4c"
- integrity sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw=
-
-lodash.map@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
- integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=
-
-lodash.merge@^4.6.2:
- version "4.6.2"
- resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
- integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-
-lodash.omit@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60"
- integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=
-
-lodash.orderby@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.orderby/-/lodash.orderby-4.6.0.tgz#e697f04ce5d78522f54d9338b32b81a3393e4eb3"
- integrity sha1-5pfwTOXXhSL1TZM4syuBozk+TrM=
-
-lodash.reduce@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
- integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=
-
-lodash.set@^4.3.2:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
- integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=
-
-lodash.size@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.size/-/lodash.size-4.2.0.tgz#71fe75ed3eabdb2bcb73a1b0b4f51c392ee27b86"
- integrity sha1-cf517T6r2yvLc6GwtPUcOS7ie4Y=
-
-lodash.sortby@^4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
- integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
-
-lodash.sum@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b"
- integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s=
-
-lodash.topairs@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.topairs/-/lodash.topairs-4.3.0.tgz#3b6deaa37d60fb116713c46c5f17ea190ec48d64"
- integrity sha1-O23qo31g+xFnE8RsXxfqGQ7EjWQ=
-
-lodash.transform@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0"
- integrity sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=
-
-lodash.union@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
- integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
-
-lodash.uniq@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
- integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-
-lodash.upperfirst@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
- integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=
-
-lodash.values@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
- integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
-
-lodash@^4.17.15:
- version "4.17.21"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
- integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-
-log-symbols@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
- integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
- dependencies:
- chalk "^4.1.0"
- is-unicode-supported "^0.1.0"
-
-lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
- integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
-
-lowercase-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
- integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
-
-lru-cache@^4.0.0:
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
- integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
- dependencies:
- pseudomap "^1.0.2"
- yallist "^2.1.2"
-
-lru-cache@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
- integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
- dependencies:
- yallist "^3.0.2"
-
-lru-cache@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
- integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
- dependencies:
- yallist "^4.0.0"
-
-macos-release@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f"
- integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==
-
-make-dir@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
- integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
- dependencies:
- semver "^6.0.0"
-
-matcher@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
- integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
- dependencies:
- escape-string-regexp "^4.0.0"
-
-merge2@^1.3.0:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
- integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-
-micromatch@4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
- integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
- dependencies:
- braces "^3.0.1"
- picomatch "^2.0.5"
-
-micromatch@^4.0.2:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
- integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
- dependencies:
- braces "^3.0.1"
- picomatch "^2.2.3"
-
-mimic-fn@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
- integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-
-mimic-response@^1.0.0, mimic-response@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
- integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
-
-mimic-response@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
- integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
-
-minimatch@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimist@^1.2.0, minimist@^1.2.5:
- version "1.2.5"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
- integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
-
-minipass@^3.0.0:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
- integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
- dependencies:
- yallist "^4.0.0"
-
-minizlib@^2.1.1:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
- integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
- dependencies:
- minipass "^3.0.0"
- yallist "^4.0.0"
-
-mkdirp@^0.5.1:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
- integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
- dependencies:
- minimist "^1.2.5"
-
-mkdirp@^1.0.3, mkdirp@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
- integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-ms@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
- integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-
-mute-stream@0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
- integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
-
-needle@2.6.0, needle@^2.3.3, needle@^2.5.0:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe"
- integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==
- dependencies:
- debug "^3.2.6"
- iconv-lite "^0.4.4"
- sax "^1.2.4"
-
-nice-try@^1.0.4:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
- integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
-
-node.extend@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-2.0.2.tgz#b4404525494acc99740f3703c496b7d5182cc6cc"
- integrity sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==
- dependencies:
- has "^1.0.3"
- is "^3.2.1"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
-
-npm-run-path@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
- integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
- dependencies:
- path-key "^2.0.0"
-
-object-hash@^2.0.3:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.1.1.tgz#9447d0279b4fcf80cff3259bf66a1dc73afabe09"
- integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==
-
-object-keys@^1.0.12, object-keys@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
- integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
-
-object.assign@^4.1.0:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
- integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
- dependencies:
- call-bind "^1.0.0"
- define-properties "^1.1.3"
- has-symbols "^1.0.1"
- object-keys "^1.1.1"
-
-once@^1.3.0, once@^1.3.1, once@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
- dependencies:
- wrappy "1"
-
-once@~1.3.0:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20"
- integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=
- dependencies:
- wrappy "1"
-
-onetime@^5.1.0:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
- integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
- dependencies:
- mimic-fn "^2.1.0"
+ css-line-break "1.1.1"
onscan.js@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/onscan.js/-/onscan.js-1.5.2.tgz#14ed636e5f4c3f0a78bacbf9a505dad3140ee341"
integrity sha512-9oGYy2gXYRjvXO9GYqqVca0VuCTAmWhbmX3egBSBP13rXiMNb+dKPJzKFEeECGqPBpf0m40Zoo+GUQ7eCackdw==
-
-open@^7.0.3:
- version "7.4.2"
- resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
- integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
- dependencies:
- is-docker "^2.0.0"
- is-wsl "^2.1.1"
-
-ora@5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f"
- integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==
- dependencies:
- bl "^4.0.3"
- chalk "^4.1.0"
- cli-cursor "^3.1.0"
- cli-spinners "^2.5.0"
- is-interactive "^1.0.0"
- log-symbols "^4.0.0"
- strip-ansi "^6.0.0"
- wcwidth "^1.0.1"
-
-os-name@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801"
- integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==
- dependencies:
- macos-release "^2.2.0"
- windows-release "^3.1.0"
-
-os-tmpdir@~1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
- integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
-
-p-cancelable@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
- integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
-
-p-cancelable@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.0.tgz#4d51c3b91f483d02a0d300765321fca393d758dd"
- integrity sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==
-
-p-finally@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
- integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
-
-p-limit@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
- integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
- dependencies:
- p-try "^2.0.0"
-
-p-map@2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
- integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
-
-p-map@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
- integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
- dependencies:
- aggregate-error "^3.0.0"
-
-p-try@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
- integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
-
-package-json@^6.3.0:
- version "6.5.0"
- resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
- integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==
- dependencies:
- got "^9.6.0"
- registry-auth-token "^4.0.0"
- registry-url "^5.0.0"
- semver "^6.2.0"
-
-pako@~0.2.0:
- version "0.2.9"
- resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
- integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=
-
-pako@~1.0.2:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
- integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
-
-parse-link-header@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-1.0.1.tgz#bedfe0d2118aeb84be75e7b025419ec8a61140a7"
- integrity sha1-vt/g0hGK64S+deewJUGeyKYRQKc=
- dependencies:
- xtend "~4.0.1"
-
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
- integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-
-path-key@^2.0.0, path-key@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
- integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-
-path-key@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
- integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-
-path-type@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
- integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-
-peek-stream@^1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67"
- integrity sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==
- dependencies:
- buffer-from "^1.0.0"
- duplexify "^3.5.0"
- through2 "^2.0.3"
-
-picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.3:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d"
- integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==
-
-pluralize@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
- integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==
-
-prepend-http@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
- integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-
-pretty-bytes@^5.1.0:
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
- integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
-
-process-nextick-args@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
- integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
-
-progress@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
- integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-
-promise-deferred@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/promise-deferred/-/promise-deferred-2.0.3.tgz#b99c9588820798501862a593d49cece51d06fd7f"
- integrity sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ==
- dependencies:
- promise "^7.3.1"
-
-promise-fs@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/promise-fs/-/promise-fs-2.1.1.tgz#0b725a592c165ff16157d1f13640ba390637e557"
- integrity sha512-43p7e4QzAQ3w6eyN0+gbBL7jXiZFWLWYITg9wIObqkBySu/a5K1EDcQ/S6UyB/bmiZWDA4NjTbcopKLTaKcGSw==
- dependencies:
- "@octetstream/promisify" "2.0.2"
-
-promise-queue@^2.2.5:
- version "2.2.5"
- resolved "https://registry.yarnpkg.com/promise-queue/-/promise-queue-2.2.5.tgz#2f6f5f7c0f6d08109e967659c79b88a9ed5e93b4"
- integrity sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=
-
-"promise@>=3.2 <8", promise@^7.3.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
- integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
- dependencies:
- asap "~2.0.3"
-
-promiseback@^2.0.2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/promiseback/-/promiseback-2.0.3.tgz#bd468d86930e8cd44bfc3292de9a6fbafb6378e6"
- integrity sha512-VZXdCwS0ppVNTIRfNsCvVwJAaP2b+pxQF7lM8DMWfmpNWyTxB6O5YNbzs+8z0ki/KIBHKHk308NTIl4kJUem3w==
- dependencies:
- is-callable "^1.1.5"
- promise-deferred "^2.0.3"
-
-proxy-from-env@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
- integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
-
-pseudomap@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
- integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
-
-pump@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
- integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
- dependencies:
- end-of-stream "^1.1.0"
- once "^1.3.1"
-
-pump@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
- integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
- dependencies:
- end-of-stream "^1.1.0"
- once "^1.3.1"
-
-pumpify@^1.3.3:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
- integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
- dependencies:
- duplexify "^3.6.0"
- inherits "^2.0.3"
- pump "^2.0.0"
-
-pupa@^2.0.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62"
- integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==
- dependencies:
- escape-goat "^2.0.0"
-
-queue-microtask@^1.2.2:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
- integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-
-queue@^6.0.1:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65"
- integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==
- dependencies:
- inherits "~2.0.3"
-
-quick-lru@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
- integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
-
-rc@^1.2.8:
- version "1.2.8"
- resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
- integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
- dependencies:
- deep-extend "^0.6.0"
- ini "~1.3.0"
- minimist "^1.2.0"
- strip-json-comments "~2.0.1"
-
-readable-stream@^2.0.0, readable-stream@~2.3.6:
- version "2.3.7"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
- integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.3"
- isarray "~1.0.0"
- process-nextick-args "~2.0.0"
- safe-buffer "~5.1.1"
- string_decoder "~1.1.1"
- util-deprecate "~1.0.1"
-
-readable-stream@^3.0.1, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
- integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
- dependencies:
- inherits "^2.0.3"
- string_decoder "^1.1.1"
- util-deprecate "^1.0.1"
-
-registry-auth-token@^4.0.0:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250"
- integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==
- dependencies:
- rc "^1.2.8"
-
-registry-url@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009"
- integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==
- dependencies:
- rc "^1.2.8"
-
-resolve-alpn@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28"
- integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==
-
-responselike@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
- integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
- dependencies:
- lowercase-keys "^1.0.0"
-
-responselike@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723"
- integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==
- dependencies:
- lowercase-keys "^2.0.0"
-
-restore-cursor@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
- integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
- dependencies:
- onetime "^5.1.0"
- signal-exit "^3.0.2"
-
-reusify@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
- integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-
-rimraf@^2.6.3:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
- integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
- dependencies:
- glob "^7.1.3"
-
-rimraf@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
- integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
- dependencies:
- glob "^7.1.3"
-
-roarr@^2.15.3:
- version "2.15.4"
- resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
- integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
- dependencies:
- boolean "^3.0.1"
- detect-node "^2.0.4"
- globalthis "^1.0.1"
- json-stringify-safe "^5.0.1"
- semver-compare "^1.0.0"
- sprintf-js "^1.1.2"
-
-run-async@^2.4.0:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
- integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
-
-run-parallel@^1.1.9:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
- integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
- dependencies:
- queue-microtask "^1.2.2"
-
-rxjs@^6.6.0:
- version "6.6.7"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
- integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
- dependencies:
- tslib "^1.9.0"
-
-safe-buffer@~5.1.0, safe-buffer@~5.1.1:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
- integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-
-safe-buffer@~5.2.0:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
- integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-
-"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@~2.1.0:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-
-sax@>=0.6.0, sax@^1.2.4:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
- integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-
-semver-compare@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
- integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
-
-semver-diff@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
- integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==
- dependencies:
- semver "^6.3.0"
-
-semver@^5.5.0:
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
- integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
-
-semver@^5.5.1:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
- integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-
-semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
- integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-
-semver@^7.0.0, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4:
- version "7.3.5"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
- integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
- dependencies:
- lru-cache "^6.0.0"
-
-serialize-error@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
- integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
- dependencies:
- type-fest "^0.13.1"
-
-set-immediate-shim@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
- integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
-
-shebang-command@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
- integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
- dependencies:
- shebang-regex "^1.0.0"
-
-shebang-command@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
- integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
- dependencies:
- shebang-regex "^3.0.0"
-
-shebang-regex@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
- integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
-
-shebang-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
- integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
-signal-exit@^3.0.0, signal-exit@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
- integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
-
-slash@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
- integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
-
-snyk-config@4.0.0, snyk-config@^4.0.0-rc.2:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-4.0.0.tgz#21d459f19087991246cc07a7ffb4501dce6f4159"
- integrity sha512-E6jNe0oUjjzVASWBOAc/mA23DhbzABDF9MI6UZvl0gylh2NSXSXw2/LjlqMNOKL2c1qkbSkzLOdIX5XACoLCAQ==
- dependencies:
- async "^3.2.0"
- debug "^4.1.1"
- lodash.merge "^4.6.2"
- minimist "^1.2.5"
-
-snyk-cpp-plugin@2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/snyk-cpp-plugin/-/snyk-cpp-plugin-2.2.1.tgz#55891511a43a6448e5a7c836a94f66f70fa705eb"
- integrity sha512-NFwVLMCqKTocY66gcim0ukF6e31VRDJqDapg5sy3vCHqlD1OCNUXSK/aI4VQEEndDrsnFmQepsL5KpEU0dDRIQ==
- dependencies:
- "@snyk/dep-graph" "^1.19.3"
- chalk "^4.1.0"
- debug "^4.1.1"
- hosted-git-info "^3.0.7"
- tslib "^2.0.0"
-
-snyk-docker-plugin@4.19.3:
- version "4.19.3"
- resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-4.19.3.tgz#14569f25c52a3fc71a20f80f5beac4ccdc326c11"
- integrity sha512-5WkXyT7uY5NrTOvEqxeMqb6dDcskT3c/gbHUTOyPuvE6tMut+OOYK8RRXbwZFeLzpS8asq4e1R7U7syYG3VXwg==
- dependencies:
- "@snyk/dep-graph" "^1.21.0"
- "@snyk/rpm-parser" "^2.0.0"
- "@snyk/snyk-docker-pull" "3.2.3"
- chalk "^2.4.2"
- debug "^4.1.1"
- docker-modem "2.1.3"
- dockerfile-ast "0.2.0"
- elfy "^1.0.0"
- event-loop-spinner "^2.0.0"
- gunzip-maybe "^1.4.2"
- mkdirp "^1.0.4"
- semver "^7.3.4"
- snyk-nodejs-lockfile-parser "1.30.2"
- tar-stream "^2.1.0"
- tmp "^0.2.1"
- tslib "^1"
- uuid "^8.2.0"
-
-snyk-go-parser@1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/snyk-go-parser/-/snyk-go-parser-1.4.1.tgz#df16a5fbd7a517ee757268ef081abc33506c8857"
- integrity sha512-StU3uHB85VMEkcgXta63M0Fgd+9cs5sMCjQXTBoYTdE4dxarPn7U67yCuwkRRdZdny1ZXtzfY8LKns9i0+dy9w==
- dependencies:
- toml "^3.0.0"
- tslib "^1.10.0"
-
-snyk-go-plugin@1.17.0:
- version "1.17.0"
- resolved "https://registry.yarnpkg.com/snyk-go-plugin/-/snyk-go-plugin-1.17.0.tgz#56d0c92d7def29ba4c3c2030c5830093e3b0dd26"
- integrity sha512-1jAYPRgMapO2BYL+HWsUq5gsAiDGmI0Pn7omc0lk24tcUOMhUB+1hb0u9WBMNzHvXBjevBkjOctjpnt2hMKN6Q==
- dependencies:
- "@snyk/dep-graph" "^1.23.1"
- "@snyk/graphlib" "2.1.9-patch.3"
- debug "^4.1.1"
- snyk-go-parser "1.4.1"
- tmp "0.2.1"
- tslib "^1.10.0"
-
-snyk-gradle-plugin@3.14.2:
- version "3.14.2"
- resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-3.14.2.tgz#898b051f679e681b6d859f0ca84a500ac028af7d"
- integrity sha512-l/nivKDZz7e2wymrwP6g2WQD8qgaYeE22SnbZrfIpwGolif81U28A9FsRedwkxKyB/shrM0vGEoD3c3zI8NLBw==
- dependencies:
- "@snyk/cli-interface" "2.11.0"
- "@snyk/dep-graph" "^1.28.0"
- "@snyk/java-call-graph-builder" "1.20.0"
- "@types/debug" "^4.1.4"
- chalk "^3.0.0"
- debug "^4.1.1"
- tmp "0.2.1"
- tslib "^2.0.0"
-
-snyk-module@3.1.0, snyk-module@^3.0.0, snyk-module@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/snyk-module/-/snyk-module-3.1.0.tgz#3e088ff473ddf0d4e253a46ea6749d76d8e6e7ba"
- integrity sha512-HHuOYEAACpUpkFgU8HT57mmxmonaJ4O3YADoSkVhnhkmJ+AowqZyJOau703dYHNrq2DvQ7qYw81H7yyxS1Nfjw==
- dependencies:
- debug "^4.1.1"
- hosted-git-info "^3.0.4"
-
-snyk-mvn-plugin@2.25.3:
- version "2.25.3"
- resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.25.3.tgz#fb7f6fa1d565b9f07c032e8b34e6308c310b2a27"
- integrity sha512-JAxOThX51JDbgMMjp3gQDVi07G9VgTYSF06QC7f5LNA0zoXNr743e2rm78RGw5bqE3JRjZxEghiLHPPuvS5DDg==
- dependencies:
- "@snyk/cli-interface" "2.11.0"
- "@snyk/dep-graph" "^1.23.1"
- "@snyk/java-call-graph-builder" "1.19.1"
- debug "^4.1.1"
- glob "^7.1.6"
- needle "^2.5.0"
- tmp "^0.1.0"
- tslib "1.11.1"
-
-snyk-nodejs-lockfile-parser@1.30.2:
- version "1.30.2"
- resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.30.2.tgz#8dbb64c42382aeaf4488c36e48c1e48eb75a1584"
- integrity sha512-wI3VXVYO/ok0uaQm5i+Koo4rKBNilYC/QRIQFlyGbZXf+WBdRcTBKVDfTy8uNfUhMRSGzd84lNclMnetU9Y+vw==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch.3"
- "@yarnpkg/lockfile" "^1.1.0"
- event-loop-spinner "^2.0.0"
- got "11.4.0"
- lodash.clonedeep "^4.5.0"
- lodash.flatmap "^4.5.0"
- lodash.isempty "^4.4.0"
- lodash.set "^4.3.2"
- lodash.topairs "^4.3.0"
- p-map "2.1.0"
- snyk-config "^4.0.0-rc.2"
- tslib "^1.9.3"
- uuid "^8.3.0"
- yaml "^1.9.2"
-
-snyk-nodejs-lockfile-parser@1.32.0:
- version "1.32.0"
- resolved "https://registry.yarnpkg.com/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.32.0.tgz#2e25ea8622ef03ae7457a93ae70e156d6c46c2ef"
- integrity sha512-FdYa/7NibnJPqBfobyw5jgI1/rd0LpMZf2W4WYYLRc2Hz7LZjKAByPjIX6qoA+lB9SC7yk5HYwWj2n4Fbg/DDw==
- dependencies:
- "@snyk/graphlib" "2.1.9-patch.3"
- "@yarnpkg/core" "^2.4.0"
- "@yarnpkg/lockfile" "^1.1.0"
- event-loop-spinner "^2.0.0"
- got "11.4.0"
- lodash.clonedeep "^4.5.0"
- lodash.flatmap "^4.5.0"
- lodash.isempty "^4.4.0"
- lodash.set "^4.3.2"
- lodash.topairs "^4.3.0"
- p-map "2.1.0"
- snyk-config "^4.0.0-rc.2"
- tslib "^1.9.3"
- uuid "^8.3.0"
- yaml "^1.9.2"
-
-snyk-nuget-plugin@1.21.1:
- version "1.21.1"
- resolved "https://registry.yarnpkg.com/snyk-nuget-plugin/-/snyk-nuget-plugin-1.21.1.tgz#a79bbc65456823a1148119873226afb0e4907ec8"
- integrity sha512-nRtedIvrow5ODqOKkQWolKrxn8ZoNL3iNJGuW0jNhwv+/9K0XE1UORM5F1ENAsd+nzCSO/kiYAXCc5CNK8HWEw==
- dependencies:
- debug "^4.1.1"
- dotnet-deps-parser "5.0.0"
- jszip "3.4.0"
- snyk-paket-parser "1.6.0"
- tslib "^1.11.2"
- xml2js "^0.4.17"
-
-snyk-paket-parser@1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/snyk-paket-parser/-/snyk-paket-parser-1.6.0.tgz#f70c423b33d31484c8c4cae74bb7f5deb9bbc382"
- integrity sha512-6htFynjBe/nakclEHUZ1A3j5Eu32/0pNve5Qm4MFn3YQmJgj7UcAO8hdyK3QfzEY29/kAv/rkJQg+SKshn+N9Q==
- dependencies:
- tslib "^1.9.3"
-
-snyk-php-plugin@1.9.2:
- version "1.9.2"
- resolved "https://registry.yarnpkg.com/snyk-php-plugin/-/snyk-php-plugin-1.9.2.tgz#282ef733060aab49da23e1fb2d2dd1af8f71f7cd"
- integrity sha512-IQcdsQBqqXVRY5DatlI7ASy4flbhtU2V7cr4P2rK9rkFnVHO6LHcitwKXVZa9ocdOmpZDzk7U6iwHJkVFcR6OA==
- dependencies:
- "@snyk/cli-interface" "^2.9.1"
- "@snyk/composer-lockfile-parser" "^1.4.1"
- tslib "1.11.1"
-
-snyk-poetry-lockfile-parser@^1.1.6:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/snyk-poetry-lockfile-parser/-/snyk-poetry-lockfile-parser-1.1.6.tgz#bab5a279c103cbcca8eb86ab87717b115592881e"
- integrity sha512-MoekbWOZPj9umfukjk2bd2o3eRj0OyO+58sxq9crMtHmTlze4h0/Uj4+fb0JFPBOtBO3c2zwbA+dvFQmpKoOTA==
- dependencies:
- "@snyk/cli-interface" "^2.9.2"
- "@snyk/dep-graph" "^1.23.0"
- debug "^4.2.0"
- toml "^3.0.0"
- tslib "^2.0.0"
-
-snyk-policy@1.19.0:
- version "1.19.0"
- resolved "https://registry.yarnpkg.com/snyk-policy/-/snyk-policy-1.19.0.tgz#0cbc442d9503970fb3afea938f57d57993a914ad"
- integrity sha512-XYjhOTRPFA7NfDUsH6uH1fbML2OgSFsqdUPbud7x01urNP9CHXgUgAD4NhKMi3dVQK+7IdYadWt0wrFWw4y+qg==
- dependencies:
- debug "^4.1.1"
- email-validator "^2.0.4"
- js-yaml "^3.13.1"
- lodash.clonedeep "^4.5.0"
- promise-fs "^2.1.1"
- semver "^6.0.0"
- snyk-module "^3.0.0"
- snyk-resolve "^1.1.0"
- snyk-try-require "^2.0.0"
-
-snyk-python-plugin@1.19.8:
- version "1.19.8"
- resolved "https://registry.yarnpkg.com/snyk-python-plugin/-/snyk-python-plugin-1.19.8.tgz#9e4dfa8ed7e16ef2752f934b786d2e033de62ce0"
- integrity sha512-LMKVnv0J4X/qHMoKB17hMND0abWtm9wdgI4xVzrOcf2Vtzs3J87trRhwLxQA2lMoBW3gcjtTeBUvNKaxikSVeQ==
- dependencies:
- "@snyk/cli-interface" "^2.0.3"
- snyk-poetry-lockfile-parser "^1.1.6"
- tmp "0.0.33"
-
-snyk-resolve-deps@4.7.2:
- version "4.7.2"
- resolved "https://registry.yarnpkg.com/snyk-resolve-deps/-/snyk-resolve-deps-4.7.2.tgz#11e7051110dadd8756819594bb30e6b88777a8b4"
- integrity sha512-Bmtr7QdRL2b3Js+mPDmvXbkprOpzO8aUFXqR0nJKAOlUVQqZ84yiuT0n/mssEiJJ0vP+k0kZvTeiTwgio4KZRg==
- dependencies:
- ansicolors "^0.3.2"
- debug "^4.1.1"
- lodash.assign "^4.2.0"
- lodash.assignin "^4.2.0"
- lodash.clone "^4.5.0"
- lodash.flatten "^4.4.0"
- lodash.get "^4.4.2"
- lodash.set "^4.3.2"
- lru-cache "^4.0.0"
- semver "^5.5.1"
- snyk-module "^3.1.0"
- snyk-resolve "^1.0.0"
- snyk-tree "^1.0.0"
- snyk-try-require "^1.1.1"
- then-fs "^2.0.0"
-
-snyk-resolve@1.1.0, snyk-resolve@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/snyk-resolve/-/snyk-resolve-1.1.0.tgz#52740cb01ba477851086855f9857b3a44296ee0e"
- integrity sha512-OZMF8I8TOu0S58Z/OS9mr8jkEzGAPByCsAkrWlcmZgPaE0RsxVKVIFPhbMNy/JlYswgGDYYIEsNw+e0j1FnTrw==
- dependencies:
- debug "^4.1.1"
- promise-fs "^2.1.1"
-
-snyk-resolve@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/snyk-resolve/-/snyk-resolve-1.0.1.tgz#eaa4a275cf7e2b579f18da5b188fe601b8eed9ab"
- integrity sha512-7+i+LLhtBo1Pkth01xv+RYJU8a67zmJ8WFFPvSxyCjdlKIcsps4hPQFebhz+0gC5rMemlaeIV6cqwqUf9PEDpw==
- dependencies:
- debug "^3.1.0"
- then-fs "^2.0.0"
-
-snyk-sbt-plugin@2.11.0:
- version "2.11.0"
- resolved "https://registry.yarnpkg.com/snyk-sbt-plugin/-/snyk-sbt-plugin-2.11.0.tgz#f5469dcf5589e34575fc901e2064475582cc3e48"
- integrity sha512-wUqHLAa3MzV6sVO+05MnV+lwc+T6o87FZZaY+43tQPytBI2Wq23O3j4POREM4fa2iFfiQJoEYD6c7xmhiEUsSA==
- dependencies:
- debug "^4.1.1"
- semver "^6.1.2"
- tmp "^0.1.0"
- tree-kill "^1.2.2"
- tslib "^1.10.0"
-
-snyk-tree@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/snyk-tree/-/snyk-tree-1.0.0.tgz#0fb73176dbf32e782f19100294160448f9111cc8"
- integrity sha1-D7cxdtvzLngvGRAClBYESPkRHMg=
- dependencies:
- archy "^1.0.0"
-
-snyk-try-require@1.3.1, snyk-try-require@^1.1.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/snyk-try-require/-/snyk-try-require-1.3.1.tgz#6e026f92e64af7fcccea1ee53d524841e418a212"
- integrity sha1-bgJvkuZK9/zM6h7lPVJIQeQYohI=
- dependencies:
- debug "^3.1.0"
- lodash.clonedeep "^4.3.0"
- lru-cache "^4.0.0"
- then-fs "^2.0.0"
-
-snyk-try-require@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/snyk-try-require/-/snyk-try-require-2.0.1.tgz#076ae9bc505d64d28389452ce19fcac28f26655a"
- integrity sha512-VCOfFIvqLMXgCXEdooQgu3A40XYIFBnj0X8Y01RJ5iAbu08b4WKGN/uAKaRVF30dABS4EcjsalmCO+YlKUPEIA==
- dependencies:
- debug "^4.1.1"
- lodash.clonedeep "^4.3.0"
- lru-cache "^5.1.1"
-
-snyk@^1.518.0:
- version "1.564.0"
- resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.564.0.tgz#c8c511128351f8b8fe239b010b6799f40bb659c5"
- integrity sha512-Fh4YusvJ9XdQfyz8JH9J8mBbipfgGLiF60MW9DYhQgP6h8z5uckAfd+S/uFMwPOVOIoe00fFo7aCLxFUuPcVJQ==
- dependencies:
- "@open-policy-agent/opa-wasm" "^1.2.0"
- "@snyk/cli-interface" "2.11.0"
- "@snyk/cloud-config-parser" "^1.9.2"
- "@snyk/code-client" "3.4.1"
- "@snyk/dep-graph" "^1.27.1"
- "@snyk/fix" "1.554.0"
- "@snyk/gemfile" "1.2.0"
- "@snyk/graphlib" "^2.1.9-patch.3"
- "@snyk/inquirer" "^7.3.3-patch"
- "@snyk/snyk-cocoapods-plugin" "2.5.2"
- "@snyk/snyk-hex-plugin" "1.1.4"
- abbrev "^1.1.1"
- ansi-escapes "3.2.0"
- chalk "^2.4.2"
- cli-spinner "0.2.10"
- configstore "^5.0.1"
- debug "^4.1.1"
- diff "^4.0.1"
- global-agent "^2.1.12"
- lodash.assign "^4.2.0"
- lodash.camelcase "^4.3.0"
- lodash.clonedeep "^4.5.0"
- lodash.endswith "^4.2.1"
- lodash.flatten "^4.4.0"
- lodash.flattendeep "^4.4.0"
- lodash.get "^4.4.2"
- lodash.groupby "^4.6.0"
- lodash.isempty "^4.4.0"
- lodash.isobject "^3.0.2"
- lodash.map "^4.6.0"
- lodash.omit "^4.5.0"
- lodash.orderby "^4.6.0"
- lodash.sortby "^4.7.0"
- lodash.uniq "^4.5.0"
- lodash.upperfirst "^4.3.1"
- lodash.values "^4.3.0"
- micromatch "4.0.2"
- needle "2.6.0"
- open "^7.0.3"
- ora "5.3.0"
- os-name "^3.0.0"
- promise-queue "^2.2.5"
- proxy-from-env "^1.0.0"
- rimraf "^2.6.3"
- semver "^6.0.0"
- snyk-config "4.0.0"
- snyk-cpp-plugin "2.2.1"
- snyk-docker-plugin "4.19.3"
- snyk-go-plugin "1.17.0"
- snyk-gradle-plugin "3.14.2"
- snyk-module "3.1.0"
- snyk-mvn-plugin "2.25.3"
- snyk-nodejs-lockfile-parser "1.32.0"
- snyk-nuget-plugin "1.21.1"
- snyk-php-plugin "1.9.2"
- snyk-policy "1.19.0"
- snyk-python-plugin "1.19.8"
- snyk-resolve "1.1.0"
- snyk-resolve-deps "4.7.2"
- snyk-sbt-plugin "2.11.0"
- snyk-tree "^1.0.0"
- snyk-try-require "1.3.1"
- source-map-support "^0.5.11"
- strip-ansi "^5.2.0"
- tar "^6.1.0"
- tempfile "^2.0.0"
- update-notifier "^4.1.0"
- uuid "^3.3.2"
- wrap-ansi "^5.1.0"
-
-source-map-support@^0.5.11, source-map-support@^0.5.7:
- version "0.5.16"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
- integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
- dependencies:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
-source-map@^0.6.0:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
- integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
-split-ca@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6"
- integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=
-
-sprintf-js@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
- integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
-
-sprintf-js@~1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
- integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-
-ssh2-streams@~0.4.10:
- version "0.4.10"
- resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
- integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
- dependencies:
- asn1 "~0.2.0"
- bcrypt-pbkdf "^1.0.2"
- streamsearch "~0.1.2"
-
-ssh2@^0.8.7:
- version "0.8.9"
- resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3"
- integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==
- dependencies:
- ssh2-streams "~0.4.10"
-
-stream-buffers@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521"
- integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==
-
-stream-shift@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
- integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-
-stream-to-array@~2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353"
- integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=
- dependencies:
- any-promise "^1.1.0"
-
-stream-to-promise@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/stream-to-promise/-/stream-to-promise-2.2.0.tgz#b1edb2e1c8cb11289d1b503c08d3f2aef51e650f"
- integrity sha1-se2y4cjLESidG1A8CNPyrvUeZQ8=
- dependencies:
- any-promise "~1.3.0"
- end-of-stream "~1.1.0"
- stream-to-array "~2.3.0"
-
-streamsearch@~0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
- integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
-
-string-width@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
- integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
- dependencies:
- emoji-regex "^7.0.1"
- is-fullwidth-code-point "^2.0.0"
- strip-ansi "^5.1.0"
-
-string-width@^4.0.0, string-width@^4.1.0:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
- integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.0"
-
-string_decoder@^1.1.1:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
- integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
- dependencies:
- safe-buffer "~5.2.0"
-
-string_decoder@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
- integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
- dependencies:
- safe-buffer "~5.1.0"
-
-strip-ansi@6.0.0, strip-ansi@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
- integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
- dependencies:
- ansi-regex "^5.0.0"
-
-strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
- integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
- dependencies:
- ansi-regex "^4.1.0"
-
-strip-eof@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
- integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
-
-strip-json-comments@~2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
- integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
-
-supports-color@^5.3.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
- integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
- dependencies:
- has-flag "^3.0.0"
-
-supports-color@^7.1.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
- integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
- dependencies:
- has-flag "^4.0.0"
-
-tar-stream@^2.0.1, tar-stream@^2.1.2:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
- integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
- dependencies:
- bl "^4.0.3"
- end-of-stream "^1.4.1"
- fs-constants "^1.0.0"
- inherits "^2.0.3"
- readable-stream "^3.1.1"
-
-tar-stream@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
- integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==
- dependencies:
- bl "^3.0.0"
- end-of-stream "^1.4.1"
- fs-constants "^1.0.0"
- inherits "^2.0.3"
- readable-stream "^3.1.1"
-
-tar@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83"
- integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==
- dependencies:
- chownr "^2.0.0"
- fs-minipass "^2.0.0"
- minipass "^3.0.0"
- minizlib "^2.1.1"
- mkdirp "^1.0.3"
- yallist "^4.0.0"
-
-temp-dir@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
- integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=
-
-temp-dir@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
- integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==
-
-tempfile@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265"
- integrity sha1-awRGhWqbERTRhW/8vlCczLCXcmU=
- dependencies:
- temp-dir "^1.0.0"
- uuid "^3.0.1"
-
-term-size@^2.1.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
- integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
-
-then-fs@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/then-fs/-/then-fs-2.0.0.tgz#72f792dd9d31705a91ae19ebfcf8b3f968c81da2"
- integrity sha1-cveS3Z0xcFqRrhnr/Piz+WjIHaI=
- dependencies:
- promise ">=3.2 <8"
-
-through2@^2.0.3:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
- integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
- dependencies:
- readable-stream "~2.3.6"
- xtend "~4.0.1"
-
-through@^2.3.6:
- version "2.3.8"
- resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
- integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
-
-tmp@0.0.33, tmp@^0.0.33:
- version "0.0.33"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
- integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
- dependencies:
- os-tmpdir "~1.0.2"
-
-tmp@0.2.1, tmp@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
- integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
- dependencies:
- rimraf "^3.0.0"
-
-tmp@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877"
- integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==
- dependencies:
- rimraf "^2.6.3"
-
-to-readable-stream@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
- integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
-
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
-toml@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
- integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
-
-tree-kill@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
- integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
-
-treeify@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8"
- integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==
-
-tslib@1.11.1:
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
- integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
-
-tslib@^1, tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
- integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
-
-tslib@^1.11.2, tslib@^1.13.0:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
-tslib@^2.0.0, tslib@^2.1.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
- integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
-
-tunnel@^0.0.6:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
- integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
-
-tweetnacl@^0.14.3:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
- integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-
-type-fest@^0.13.1:
- version "0.13.1"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
- integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
-
-type-fest@^0.21.3:
- version "0.21.3"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
- integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
-
-type-fest@^0.8.1:
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
- integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
-
-typedarray-to-buffer@^3.1.5:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
- integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
- dependencies:
- is-typedarray "^1.0.0"
-
-unique-string@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
- integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==
- dependencies:
- crypto-random-string "^2.0.0"
-
-upath@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"
- integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
-
-update-notifier@^4.1.0:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3"
- integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==
- dependencies:
- boxen "^4.2.0"
- chalk "^3.0.0"
- configstore "^5.0.1"
- has-yarn "^2.1.0"
- import-lazy "^2.1.0"
- is-ci "^2.0.0"
- is-installed-globally "^0.3.1"
- is-npm "^4.0.0"
- is-yarn-global "^0.3.0"
- latest-version "^5.0.0"
- pupa "^2.0.1"
- semver-diff "^3.1.1"
- xdg-basedir "^4.0.0"
-
-url-parse-lax@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
- integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
- dependencies:
- prepend-http "^2.0.0"
-
-utf8@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
- integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==
-
-util-deprecate@^1.0.1, util-deprecate@~1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
- integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-
-uuid@^3.0.1, uuid@^3.3.2:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
-uuid@^8.2.0, uuid@^8.3.0, uuid@^8.3.2:
- version "8.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
- integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
-
-vscode-languageserver-types@^3.16.0:
- version "3.16.0"
- resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
- integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
-
-wcwidth@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
- integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
- dependencies:
- defaults "^1.0.3"
-
-which@^1.2.9:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
- integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
- dependencies:
- isexe "^2.0.0"
-
-which@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
-widest-line@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
- integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==
- dependencies:
- string-width "^4.0.0"
-
-windows-release@^3.1.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f"
- integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==
- dependencies:
- execa "^1.0.0"
-
-wrap-ansi@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
- integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
- dependencies:
- ansi-styles "^3.2.0"
- string-width "^3.0.0"
- strip-ansi "^5.0.0"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-
-write-file-atomic@^3.0.0:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
- integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
- dependencies:
- imurmurhash "^0.1.4"
- is-typedarray "^1.0.0"
- signal-exit "^3.0.2"
- typedarray-to-buffer "^3.1.5"
-
-xdg-basedir@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
- integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
-
-xml-js@^1.6.11:
- version "1.6.11"
- resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9"
- integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==
- dependencies:
- sax "^1.2.4"
-
-xml2js@0.4.23, xml2js@^0.4.17:
- version "0.4.23"
- resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
- integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
- dependencies:
- sax ">=0.6.0"
- xmlbuilder "~11.0.0"
-
-xmlbuilder@~11.0.0:
- version "11.0.1"
- resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
- integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
-
-xtend@~4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
- integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
-
-yallist@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
- integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
-
-yallist@^3.0.2:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
- integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
-
-yallist@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
- integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-
-yaml-js@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/yaml-js/-/yaml-js-0.3.0.tgz#ad0893d9de881a93fd6bf936e8d89cdde309e848"
- integrity sha512-JbTUdsPiCkOyz+JOSqAVc19omTnUBnBQglhuclYov5HpWbEOz8y+ftqWjiMa9Pe/eF/dmCUeNgVs/VWg53GlgQ==
-
-yaml@^1.9.2:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
- integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==